Public Access
Add optional Serene Seasons Age policies and seasonal pages (#38)
Build / build (push) Canceled after 0s
Build / build (push) Canceled after 0s
This commit is contained in:
1 parent
4541cbbba5
commit
6452d2c324
29 files changed
+399
-8
No files matched your search
@@ -18,6 +18,7 @@ import net.dnorberg.rivencraft.command.RivencraftCommands;
|
||||
import net.dnorberg.rivencraft.network.ModNetworking;
|
||||
import net.dnorberg.rivencraft.config.RivencraftConfig;
|
||||
import net.dnorberg.rivencraft.config.RivencraftClientConfig;
|
||||
import net.dnorberg.rivencraft.compat.SereneSeasonsCompatibility;
|
||||
import net.dnorberg.rivencraft.book.AgeDimensionService;
|
||||
import net.dnorberg.rivencraft.book.AgeWeatherService;
|
||||
import net.dnorberg.rivencraft.book.AgeRuntimeProfiles;
|
||||
@@ -51,6 +52,7 @@ public final class Rivencraft {
|
||||
ModCreativeTabs.register(modEventBus);
|
||||
NeoForge.EVENT_BUS.addListener(BookStandConversionHandler::onRightClickBlock);
|
||||
NeoForge.EVENT_BUS.addListener(AgeWeatherService::onLevelTickPost);
|
||||
NeoForge.EVENT_BUS.addListener(SereneSeasonsCompatibility::onLevelTickPost);
|
||||
NeoForge.EVENT_BUS.addListener(AgeCelestialAngleSync::onPlayerLoggedIn);
|
||||
NeoForge.EVENT_BUS.addListener(AgeCelestialAngleSync::onPlayerChangedDimension);
|
||||
NeoForge.EVENT_BUS.addListener(AgeLifecycleEvents::onPlayerLoggedOut);
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.util.Comparator;
|
||||
import java.util.Random;
|
||||
import net.dnorberg.rivencraft.Rivencraft;
|
||||
import net.dnorberg.rivencraft.compat.ImmersivePortalsChunkLoaders;
|
||||
import net.dnorberg.rivencraft.compat.SereneSeasonsCompatibility;
|
||||
import net.dnorberg.rivencraft.writing.OrderedWriting;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.GlobalPos;
|
||||
@@ -74,6 +75,7 @@ public final class AgeDimensionService {
|
||||
);
|
||||
if (level != null) {
|
||||
applyCelestialClock(level, age);
|
||||
SereneSeasonsCompatibility.registerAge(server, age);
|
||||
}
|
||||
}
|
||||
synchronizeImmersivePortals(server);
|
||||
@@ -110,6 +112,7 @@ public final class AgeDimensionService {
|
||||
ServerLevel existing = server.getLevel(key);
|
||||
if (existing != null) {
|
||||
applyCelestialClock(existing, definition);
|
||||
SereneSeasonsCompatibility.registerAge(server, definition);
|
||||
return existing;
|
||||
}
|
||||
|
||||
@@ -125,6 +128,7 @@ public final class AgeDimensionService {
|
||||
Rivencraft.LOGGER.error("Failed to load Age {} as {}", definition.id(), definition.dimensionId());
|
||||
} else {
|
||||
applyCelestialClock(loaded, definition);
|
||||
SereneSeasonsCompatibility.registerAge(server, definition);
|
||||
synchronizeImmersivePortals(server);
|
||||
}
|
||||
return loaded;
|
||||
|
||||
@@ -71,6 +71,7 @@ public record AgeGenerationPlan(int version, long ageSeed, OrderedWriting writin
|
||||
symbol.index(), symbol.reference(), symbol.semantics())).toList(),
|
||||
profile, biomes);
|
||||
AgeEnvironmentCompiler.apply(plan, traits);
|
||||
AgeSeasonMode.validate(plan);
|
||||
return plan;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package net.dnorberg.rivencraft.book;
|
||||
|
||||
import java.util.Locale;
|
||||
import net.dnorberg.rivencraft.writing.SymbolSemantics;
|
||||
import net.neoforged.fml.ModList;
|
||||
|
||||
/** One authored season policy per Age; unspecified Ages retain the normal cycle. */
|
||||
public enum AgeSeasonMode {
|
||||
NO_SEASONS,
|
||||
SEASONS,
|
||||
ETERNAL_WINTER,
|
||||
ETERNAL_SUMMER,
|
||||
ETERNAL_FALL,
|
||||
ETERNAL_SPRING;
|
||||
|
||||
public static AgeSeasonMode from(AgeGenerationPlan plan) {
|
||||
if (plan == null) return SEASONS;
|
||||
return plan.symbols().stream()
|
||||
.filter(symbol -> symbol.semantics().role() == SymbolSemantics.Role.SEASON)
|
||||
.map(symbol -> valueOf(((SymbolSemantics.ChoiceValue) symbol.semantics().value())
|
||||
.id().toUpperCase(Locale.ROOT)))
|
||||
.findFirst().orElse(SEASONS);
|
||||
}
|
||||
|
||||
public static void validate(AgeGenerationPlan plan) {
|
||||
if (plan == null) return;
|
||||
boolean authored = plan.symbols().stream()
|
||||
.anyMatch(symbol -> symbol.semantics().role() == SymbolSemantics.Role.SEASON);
|
||||
if (authored && from(plan) != NO_SEASONS
|
||||
&& !ModList.get().isLoaded("sereneseasons")) {
|
||||
throw new IllegalArgumentException("This seasonal page requires Serene Seasons to create the Age");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean eternal() {
|
||||
return this != NO_SEASONS && this != SEASONS;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import net.dnorberg.rivencraft.writing.PageContent;
|
||||
import net.dnorberg.rivencraft.writing.SymbolCatalog;
|
||||
import net.dnorberg.rivencraft.writing.SymbolReference;
|
||||
import net.dnorberg.rivencraft.writing.WritingEntry;
|
||||
import net.dnorberg.rivencraft.writing.WritingGrammar;
|
||||
import net.minecraft.gametest.framework.GameTest;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -51,6 +52,8 @@ public final class EnvironmentWritingGameTests {
|
||||
"sky/overworld", "sky/nether", "sky/end",
|
||||
"weather/normal", "weather/fast", "weather/slow", "weather/off",
|
||||
"weather/always", "weather/storm", "celestial/normal_time",
|
||||
"season/no_seasons", "season/seasons", "season/eternal_winter",
|
||||
"season/eternal_summer", "season/eternal_fall", "season/eternal_spring",
|
||||
"period/zero", "period/half", "period/full", "period/double",
|
||||
"phase/nadir", "phase/rising", "phase/zenith", "phase/setting");
|
||||
Set<String> creative = PageItem.creativeSymbolPages(helper.getLevel().registryAccess()).stream()
|
||||
@@ -73,6 +76,23 @@ public final class EnvironmentWritingGameTests {
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
@GameTest(template = "empty")
|
||||
public static void seasonalPagesAreExclusiveAndPersist(GameTestHelper helper) {
|
||||
OrderedWriting noSeasons = new OrderedWriting(List.of(
|
||||
WritingEntry.linkPanel(), page("season/no_seasons")));
|
||||
var plan = AgeGenerationPlan.compile(noSeasons, 42L, AgeGenerationProfiles.STANDARD,
|
||||
AgeTraits.select(42L), helper.getLevel().registryAccess());
|
||||
helper.assertValueEqual(AgeSeasonMode.from(plan), AgeSeasonMode.NO_SEASONS,
|
||||
"No Seasons must compile without an installed season mod");
|
||||
helper.assertValueEqual(AgeSeasonMode.from(AgeGenerationPlan.load(plan.save())),
|
||||
AgeSeasonMode.NO_SEASONS, "The Age's seasonal policy must survive reload");
|
||||
OrderedWriting conflict = new OrderedWriting(List.of(WritingEntry.linkPanel(),
|
||||
page("season/no_seasons"), page("season/eternal_winter")));
|
||||
helper.assertFalse(WritingGrammar.parse(conflict).canCreate(),
|
||||
"Only one seasonal page may be used in an Age");
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
@GameTest(template = "empty")
|
||||
public static void writtenEnvironmentPersistsAndPageFreeAgesStaySeeded(GameTestHelper helper) {
|
||||
OrderedWriting writing = new OrderedWriting(List.of(
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package net.dnorberg.rivencraft.client;
|
||||
|
||||
import net.dnorberg.rivencraft.compat.SereneSeasonsCompatibility;
|
||||
import net.dnorberg.rivencraft.network.SereneAgeWhitelistPayload;
|
||||
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||
|
||||
/** Applies incremental Serene whitelist updates on the client main thread. */
|
||||
public final class ClientSereneAgeWhitelist {
|
||||
private ClientSereneAgeWhitelist() {
|
||||
}
|
||||
|
||||
public static void handle(SereneAgeWhitelistPayload payload, IPayloadContext context) {
|
||||
context.enqueueWork(() -> SereneSeasonsCompatibility.acceptClient(payload.dimensionId(), payload.enabled()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package net.dnorberg.rivencraft.compat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.dnorberg.rivencraft.book.AgeSeasonMode;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import sereneseasons.api.season.Season;
|
||||
import sereneseasons.config.SeasonsConfig;
|
||||
import sereneseasons.init.ModConfig;
|
||||
import sereneseasons.season.SeasonHandler;
|
||||
import sereneseasons.season.SeasonSavedData;
|
||||
import sereneseasons.season.SeasonTime;
|
||||
|
||||
/** Isolates references to optional Serene Seasons and GlitchCore classes. */
|
||||
final class SereneSeasonsAdapter {
|
||||
private static final String WHITELIST_KEY = "dimension_settings.whitelisted_dimensions";
|
||||
|
||||
private SereneSeasonsAdapter() {
|
||||
}
|
||||
|
||||
static boolean setServerWhitelist(ResourceLocation dimensionId, boolean enabled) {
|
||||
SeasonsConfig config = ModConfig.seasons;
|
||||
if (config == null || config.whitelistedDimensions.contains(dimensionId.toString()) == enabled) return false;
|
||||
List<String> dimensions = new ArrayList<>(config.whitelistedDimensions);
|
||||
if (enabled) dimensions.add(dimensionId.toString());
|
||||
else dimensions.removeIf(dimensionId.toString()::equals);
|
||||
// GlitchCore rereads this config from disk before synchronizing new clients.
|
||||
config.set(WHITELIST_KEY, dimensions);
|
||||
config.write();
|
||||
config.whitelistedDimensions = dimensions;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void setClientWhitelist(ResourceLocation dimensionId, boolean enabled) {
|
||||
SeasonsConfig config = ModConfig.seasons;
|
||||
if (config == null || config.whitelistedDimensions.contains(dimensionId.toString()) == enabled) return;
|
||||
List<String> dimensions = new ArrayList<>(config.whitelistedDimensions);
|
||||
if (enabled) dimensions.add(dimensionId.toString());
|
||||
else dimensions.removeIf(dimensionId.toString()::equals);
|
||||
config.whitelistedDimensions = dimensions;
|
||||
}
|
||||
|
||||
static void holdSeason(ServerLevel level, AgeSeasonMode mode) {
|
||||
SeasonSavedData data = SeasonHandler.getSeasonSavedData(level);
|
||||
Season.SubSeason target = switch (mode) {
|
||||
case ETERNAL_SPRING -> Season.SubSeason.MID_SPRING;
|
||||
case ETERNAL_SUMMER -> Season.SubSeason.MID_SUMMER;
|
||||
case ETERNAL_FALL -> Season.SubSeason.MID_AUTUMN;
|
||||
case ETERNAL_WINTER -> Season.SubSeason.MID_WINTER;
|
||||
default -> throw new IllegalArgumentException("Not an eternal season: " + mode);
|
||||
};
|
||||
int duration = SeasonTime.ZERO.getSubSeasonDuration();
|
||||
int midpoint = target.ordinal() * duration + duration / 2;
|
||||
// Reset well before the end of MID_* so the client never observes a
|
||||
// different sub-season; avoid marking saved data dirty every tick.
|
||||
int current = data.seasonCycleTicks;
|
||||
if (current >= midpoint && current < midpoint + duration / 4) return;
|
||||
data.seasonCycleTicks = midpoint;
|
||||
data.setDirty();
|
||||
SeasonHandler.sendSeasonUpdate(level);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package net.dnorberg.rivencraft.compat;
|
||||
|
||||
import net.dnorberg.rivencraft.Rivencraft;
|
||||
import net.dnorberg.rivencraft.book.AgeDefinition;
|
||||
import net.dnorberg.rivencraft.book.AgeDimensionService;
|
||||
import net.dnorberg.rivencraft.book.AgeRegistrySavedData;
|
||||
import net.dnorberg.rivencraft.book.AgeSeasonMode;
|
||||
import net.dnorberg.rivencraft.config.RivencraftConfig;
|
||||
import net.dnorberg.rivencraft.network.SereneAgeWhitelistPayload;
|
||||
import net.dnorberg.rivencraft.writing.SymbolSemantics;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.neoforged.fml.ModList;
|
||||
import net.neoforged.neoforge.network.PacketDistributor;
|
||||
import net.neoforged.neoforge.network.registration.NetworkRegistry;
|
||||
import net.neoforged.neoforge.event.tick.LevelTickEvent;
|
||||
|
||||
/** Optional, guarded bridge for Serene Seasons' per-dimension whitelist. */
|
||||
public final class SereneSeasonsCompatibility {
|
||||
private SereneSeasonsCompatibility() {
|
||||
}
|
||||
|
||||
public static void registerAge(MinecraftServer server, AgeDefinition age) {
|
||||
if (!available() || !isAge(age.dimensionId())) return;
|
||||
AgeSeasonMode mode = AgeSeasonMode.from(age.plan());
|
||||
boolean explicit = age.plan() != null && age.plan().symbols().stream()
|
||||
.anyMatch(symbol -> symbol.semantics().role() == SymbolSemantics.Role.SEASON);
|
||||
if (mode != AgeSeasonMode.NO_SEASONS && !explicit && !RivencraftConfig.autoWhitelistSereneAges()) {
|
||||
return;
|
||||
}
|
||||
boolean enabled = mode != AgeSeasonMode.NO_SEASONS;
|
||||
try {
|
||||
if (SereneSeasonsAdapter.setServerWhitelist(age.dimensionId(), enabled)) {
|
||||
for (ServerPlayer player : server.getPlayerList().getPlayers()) {
|
||||
send(player, age.dimensionId(), enabled);
|
||||
}
|
||||
}
|
||||
if (enabled && mode.eternal()) {
|
||||
ServerLevel level = server.getLevel(AgeDimensionService.dimensionKey(age));
|
||||
if (level != null) SereneSeasonsAdapter.holdSeason(level, mode);
|
||||
}
|
||||
} catch (RuntimeException exception) {
|
||||
Rivencraft.LOGGER.warn("Could not apply Serene Seasons policy for Age {}", age.dimensionId(), exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onLevelTickPost(LevelTickEvent.Post event) {
|
||||
if (!(event.getLevel() instanceof ServerLevel level) || !available()
|
||||
|| !isAge(level.dimension().location())) return;
|
||||
AgeDefinition age = AgeRegistrySavedData.get(level)
|
||||
.findByDimension(level.dimension().location()).orElse(null);
|
||||
if (age == null) return;
|
||||
AgeSeasonMode mode = AgeSeasonMode.from(age.plan());
|
||||
if (!mode.eternal()) return;
|
||||
try {
|
||||
SereneSeasonsAdapter.holdSeason(level, mode);
|
||||
} catch (RuntimeException exception) {
|
||||
Rivencraft.LOGGER.warn("Could not hold Serene season in Age {}", age.dimensionId(), exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static void acceptClient(ResourceLocation dimensionId, boolean enabled) {
|
||||
if (available() && isAge(dimensionId)) {
|
||||
SereneSeasonsAdapter.setClientWhitelist(dimensionId, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
private static void send(ServerPlayer player, ResourceLocation dimensionId, boolean enabled) {
|
||||
if (NetworkRegistry.hasChannel(player.connection, SereneAgeWhitelistPayload.TYPE.id())) {
|
||||
PacketDistributor.sendToPlayer(player, new SereneAgeWhitelistPayload(dimensionId, enabled));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean available() {
|
||||
return ModList.get().isLoaded("sereneseasons");
|
||||
}
|
||||
|
||||
private static boolean isAge(ResourceLocation dimensionId) {
|
||||
return dimensionId.getNamespace().equals(Rivencraft.MOD_ID)
|
||||
&& dimensionId.getPath().startsWith("age/");
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ public final class RivencraftConfig {
|
||||
private static final ModConfigSpec.EnumValue<ReturnLinkMode> RETURN_LINK_MODE;
|
||||
private static final ModConfigSpec.IntValue SYMBOL_INK_COST;
|
||||
private static final ModConfigSpec.IntValue LINK_PANEL_INK_COST;
|
||||
private static final ModConfigSpec.BooleanValue AUTO_WHITELIST_SERENE_AGES;
|
||||
|
||||
static {
|
||||
ModConfigSpec.Builder builder = new ModConfigSpec.Builder();
|
||||
@@ -33,6 +34,12 @@ public final class RivencraftConfig {
|
||||
LINK_PANEL_INK_COST = builder.comment("Ink units consumed when preparing one baseline link-panel page.")
|
||||
.defineInRange("link_panel_ink_cost", 2, 1, 16);
|
||||
builder.pop();
|
||||
builder.push("serene_seasons");
|
||||
AUTO_WHITELIST_SERENE_AGES = builder.comment(
|
||||
"Automatically add Rivencraft Ages to Serene Seasons' dimension whitelist when loaded.",
|
||||
"Existing entries are preserved; disabling this does not remove previously added Ages."
|
||||
).define("auto_whitelist_ages", true);
|
||||
builder.pop();
|
||||
SPEC = builder.build();
|
||||
}
|
||||
|
||||
@@ -50,4 +57,8 @@ public final class RivencraftConfig {
|
||||
public static int linkPanelInkCost() {
|
||||
return LINK_PANEL_INK_COST.get();
|
||||
}
|
||||
|
||||
public static boolean autoWhitelistSereneAges() {
|
||||
return AUTO_WHITELIST_SERENE_AGES.get();
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import net.dnorberg.rivencraft.block.entity.BookStandBlockEntity;
|
||||
import net.dnorberg.rivencraft.client.ClientLinkPreviewHandler;
|
||||
import net.dnorberg.rivencraft.client.ClientBookInteractionHandler;
|
||||
import net.dnorberg.rivencraft.client.ClientAgeCelestialAngles;
|
||||
import net.dnorberg.rivencraft.client.ClientSereneAgeWhitelist;
|
||||
import net.dnorberg.rivencraft.client.ClientWritingDeskCatalog;
|
||||
import net.dnorberg.rivencraft.event.AgeCelestialAngleSync;
|
||||
import net.dnorberg.rivencraft.item.DescriptiveBookItem;
|
||||
@@ -78,6 +79,8 @@ public final class ModNetworking {
|
||||
ClientLinkPreviewHandler::handle);
|
||||
registrar.playToClient(AgeCelestialAnglePayload.TYPE, AgeCelestialAnglePayload.STREAM_CODEC,
|
||||
ClientAgeCelestialAngles::handle);
|
||||
registrar.playToClient(SereneAgeWhitelistPayload.TYPE, SereneAgeWhitelistPayload.STREAM_CODEC,
|
||||
ClientSereneAgeWhitelist::handle);
|
||||
registrar.playToClient(WritingDeskCatalogPayload.TYPE, WritingDeskCatalogPayload.STREAM_CODEC,
|
||||
ClientWritingDeskCatalog::handle);
|
||||
registrar.playToServer(SetBookBinderTitlePayload.TYPE, SetBookBinderTitlePayload.STREAM_CODEC,
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package net.dnorberg.rivencraft.network;
|
||||
|
||||
import net.dnorberg.rivencraft.Rivencraft;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/** Updates an already connected client's Serene whitelist for one Age. */
|
||||
public record SereneAgeWhitelistPayload(ResourceLocation dimensionId, boolean enabled) implements CustomPacketPayload {
|
||||
public static final Type<SereneAgeWhitelistPayload> TYPE = new Type<>(
|
||||
ResourceLocation.fromNamespaceAndPath(Rivencraft.MOD_ID, "serene_age_whitelist"));
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, SereneAgeWhitelistPayload> STREAM_CODEC =
|
||||
StreamCodec.of(
|
||||
(buffer, payload) -> {
|
||||
buffer.writeResourceLocation(payload.dimensionId());
|
||||
buffer.writeBoolean(payload.enabled());
|
||||
},
|
||||
buffer -> new SereneAgeWhitelistPayload(buffer.readResourceLocation(), buffer.readBoolean()));
|
||||
|
||||
@Override
|
||||
public Type<? extends CustomPacketPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ public final class ModCreativeTabs {
|
||||
.build());
|
||||
|
||||
private static final List<String> CATEGORY_ORDER = List.of(
|
||||
"terrain", "biome", "cave_biome", "biome_generation", "weather", "lighting", "sky", "celestial", "block", "fluid", "feature");
|
||||
"terrain", "biome", "cave_biome", "biome_generation", "weather", "season", "lighting", "sky", "celestial", "block", "fluid", "feature");
|
||||
|
||||
public static final Map<ResourceLocation, DeferredHolder<CreativeModeTab, CreativeModeTab>> SYMBOL_PAGES =
|
||||
registerSymbolPageTabs();
|
||||
|
||||
@@ -80,6 +80,8 @@ public record SymbolSemantics(Role role, Value value) {
|
||||
SKY("sky", Kind.CHOICE, "overworld", "nether", "end"),
|
||||
WEATHER("weather", Kind.CHOICE,
|
||||
"normal", "fast", "slow", "off", "always", "cloudy", "rain", "snow", "storm"),
|
||||
SEASON("season", Kind.CHOICE,
|
||||
"no_seasons", "seasons", "eternal_winter", "eternal_summer", "eternal_fall", "eternal_spring"),
|
||||
CELESTIAL("celestial", Kind.CHOICE, "normal_time"),
|
||||
PERIOD_MODIFIER("period_modifier", Kind.NUMBER),
|
||||
ANGLE_MODIFIER("angle_modifier", Kind.NUMBER),
|
||||
|
||||
@@ -152,7 +152,7 @@ public final class WritingGrammar {
|
||||
|
||||
private static boolean singular(Role role) {
|
||||
return switch (role) {
|
||||
case TERRAIN, BIOME_CONTROLLER, WEATHER, LIGHTING, SKY, CELESTIAL -> true;
|
||||
case TERRAIN, BIOME_CONTROLLER, WEATHER, SEASON, LIGHTING, SKY, CELESTIAL -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
@@ -167,11 +167,12 @@ public final class WritingGrammar {
|
||||
case BIOME -> 1;
|
||||
case BIOME_CONTROLLER -> 2;
|
||||
case WEATHER -> 3;
|
||||
case LIGHTING -> 4;
|
||||
case SKY -> 5;
|
||||
case PERIOD_MODIFIER, ANGLE_MODIFIER, PHASE_MODIFIER -> 6;
|
||||
case CELESTIAL -> 7;
|
||||
case BLOCK, FEATURE -> 8;
|
||||
case SEASON -> 4;
|
||||
case LIGHTING -> 5;
|
||||
case SKY -> 6;
|
||||
case PERIOD_MODIFIER, ANGLE_MODIFIER, PHASE_MODIFIER -> 7;
|
||||
case CELESTIAL -> 8;
|
||||
case BLOCK, FEATURE -> 9;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -195,6 +195,18 @@
|
||||
"symbol.rivencraft.weather.always.description": "Keep the Age rainy.",
|
||||
"symbol.rivencraft.weather.storm": "Storm Weather",
|
||||
"symbol.rivencraft.weather.storm.description": "Keep the Age rainy and thundering.",
|
||||
"symbol.rivencraft.season.no_seasons": "No Seasons",
|
||||
"symbol.rivencraft.season.no_seasons.description": "Keep seasonal changes out of this Age.",
|
||||
"symbol.rivencraft.season.seasons": "Seasons",
|
||||
"symbol.rivencraft.season.seasons.description": "Let this Age follow its own season cycle.",
|
||||
"symbol.rivencraft.season.eternal_winter": "Eternal Winter",
|
||||
"symbol.rivencraft.season.eternal_winter.description": "Hold this Age in midwinter where the biome allows winter effects.",
|
||||
"symbol.rivencraft.season.eternal_summer": "Eternal Summer",
|
||||
"symbol.rivencraft.season.eternal_summer.description": "Hold this Age in midsummer.",
|
||||
"symbol.rivencraft.season.eternal_fall": "Eternal Fall",
|
||||
"symbol.rivencraft.season.eternal_fall.description": "Hold this Age in midautumn.",
|
||||
"symbol.rivencraft.season.eternal_spring": "Eternal Spring",
|
||||
"symbol.rivencraft.season.eternal_spring.description": "Hold this Age in midspring.",
|
||||
"symbol.rivencraft.celestial.normal_time": "Normal Time",
|
||||
"symbol.rivencraft.celestial.normal_time.description": "Use a normal-length celestial cycle.",
|
||||
"symbol.rivencraft.period.zero": "Zero Length",
|
||||
@@ -261,6 +273,7 @@
|
||||
"itemGroup.rivencraft.pages.cave_biome": "Rivencraft Cave Biome Pages",
|
||||
"itemGroup.rivencraft.pages.biome_generation": "Rivencraft Biome Generation Pages",
|
||||
"itemGroup.rivencraft.pages.weather": "Rivencraft Weather Pages",
|
||||
"itemGroup.rivencraft.pages.season": "Rivencraft Season Pages",
|
||||
"itemGroup.rivencraft.pages.lighting": "Rivencraft Lighting Pages",
|
||||
"itemGroup.rivencraft.pages.sky": "Rivencraft Sky Pages",
|
||||
"itemGroup.rivencraft.pages.celestial": "Rivencraft Celestial Pages",
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"schema_version": 2,
|
||||
"name": "symbol.rivencraft.season.eternal_fall",
|
||||
"description": "symbol.rivencraft.season.eternal_fall.description",
|
||||
"category": "rivencraft:season",
|
||||
"semantics": { "role": "season", "value": "eternal_fall" }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"schema_version": 2,
|
||||
"name": "symbol.rivencraft.season.eternal_spring",
|
||||
"description": "symbol.rivencraft.season.eternal_spring.description",
|
||||
"category": "rivencraft:season",
|
||||
"semantics": { "role": "season", "value": "eternal_spring" }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"schema_version": 2,
|
||||
"name": "symbol.rivencraft.season.eternal_summer",
|
||||
"description": "symbol.rivencraft.season.eternal_summer.description",
|
||||
"category": "rivencraft:season",
|
||||
"semantics": { "role": "season", "value": "eternal_summer" }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"schema_version": 2,
|
||||
"name": "symbol.rivencraft.season.eternal_winter",
|
||||
"description": "symbol.rivencraft.season.eternal_winter.description",
|
||||
"category": "rivencraft:season",
|
||||
"semantics": { "role": "season", "value": "eternal_winter" }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"schema_version": 2,
|
||||
"name": "symbol.rivencraft.season.no_seasons",
|
||||
"description": "symbol.rivencraft.season.no_seasons.description",
|
||||
"category": "rivencraft:season",
|
||||
"semantics": { "role": "season", "value": "no_seasons" }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"schema_version": 2,
|
||||
"name": "symbol.rivencraft.season.seasons",
|
||||
"description": "symbol.rivencraft.season.seasons.description",
|
||||
"category": "rivencraft:season",
|
||||
"semantics": { "role": "season", "value": "seasons" }
|
||||
}
|
||||
Reference in new issue
Block a user