Public Access
1 parent
733028b943
commit
73dec80ae6
6 files changed
+139
-7
No files matched your search
@@ -262,6 +262,13 @@ from Book Stands. Snapshot chunk loading is independent from whether the Explore
|
|||||||
control is enabled, so mounted books retain live terrain without allowing the
|
control is enabled, so mounted books retain live terrain without allowing the
|
||||||
player to enter Explore mode.
|
player to enter Explore mode.
|
||||||
|
|
||||||
|
Version 0.2.17.0 begins persistent random Age traits. New page-free Ages select
|
||||||
|
and save a legacy-inspired biome distribution, lighting mode, weather mode, and
|
||||||
|
celestial period, angle, and phase. The celestial ranges follow Legacy's random
|
||||||
|
symbol fallbacks: period 0.2–2.0, angle 0–360 degrees, and phase 0–1. Existing
|
||||||
|
Ages load with normal Legacy defaults instead of being rerolled. This immutable
|
||||||
|
trait layer is the foundation for applying each choice to modern worldgen.
|
||||||
|
|
||||||
Version 0.2.14.0 restores the legacy Linking and Descriptive Book artwork for
|
Version 0.2.14.0 restores the legacy Linking and Descriptive Book artwork for
|
||||||
inventory items and gives each book its matching open-book texture when placed
|
inventory items and gives each book its matching open-book texture when placed
|
||||||
on a Book Stand. Return Linking Books created inside another Age also retain and
|
on a Book Stand. Return Linking Books created inside another Age also retain and
|
||||||
|
|||||||
+1
-1
@@ -35,7 +35,7 @@ mod_name=Rivencraft
|
|||||||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||||
mod_license=LGPL-3.0-or-later
|
mod_license=LGPL-3.0-or-later
|
||||||
# The mod version. Rivencraft uses the x.y.z.b policy documented in README.md.
|
# The mod version. Rivencraft uses the x.y.z.b policy documented in README.md.
|
||||||
mod_version=0.2.16.15
|
mod_version=0.2.17.0
|
||||||
immersive_portals_version=6.0.7
|
immersive_portals_version=6.0.7
|
||||||
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
||||||
# This should match the base package used for the mod sources.
|
# This should match the base package used for the mod sources.
|
||||||
|
|||||||
@@ -15,18 +15,20 @@ public record AgeDefinition(
|
|||||||
int generationVersion,
|
int generationVersion,
|
||||||
long createdGameTime,
|
long createdGameTime,
|
||||||
ResourceLocation dimensionId,
|
ResourceLocation dimensionId,
|
||||||
BlockPos spawn
|
BlockPos spawn,
|
||||||
|
AgeTraits traits
|
||||||
) {
|
) {
|
||||||
public static final ResourceLocation RANDOM_VANILLA_PROFILE = ResourceLocation.fromNamespaceAndPath(
|
public static final ResourceLocation RANDOM_VANILLA_PROFILE = ResourceLocation.fromNamespaceAndPath(
|
||||||
Rivencraft.MOD_ID, "random_vanilla"
|
Rivencraft.MOD_ID, "random_vanilla"
|
||||||
);
|
);
|
||||||
public static final int CURRENT_GENERATION_VERSION = 2;
|
public static final int CURRENT_GENERATION_VERSION = 3;
|
||||||
|
|
||||||
public AgeDefinition {
|
public AgeDefinition {
|
||||||
Objects.requireNonNull(id, "id");
|
Objects.requireNonNull(id, "id");
|
||||||
Objects.requireNonNull(name, "name");
|
Objects.requireNonNull(name, "name");
|
||||||
Objects.requireNonNull(generationProfile, "generationProfile");
|
Objects.requireNonNull(generationProfile, "generationProfile");
|
||||||
Objects.requireNonNull(dimensionId, "dimensionId");
|
Objects.requireNonNull(dimensionId, "dimensionId");
|
||||||
|
Objects.requireNonNull(traits, "traits");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResourceLocation dimensionId(UUID ageId) {
|
public static ResourceLocation dimensionId(UUID ageId) {
|
||||||
@@ -57,6 +59,6 @@ public record AgeDefinition(
|
|||||||
|
|
||||||
public AgeDefinition withSpawn(BlockPos spawn) {
|
public AgeDefinition withSpawn(BlockPos spawn) {
|
||||||
return new AgeDefinition(id, name, seed, generationProfile, generationVersion,
|
return new AgeDefinition(id, name, seed, generationProfile, generationVersion,
|
||||||
createdGameTime, dimensionId, spawn.immutable());
|
createdGameTime, dimensionId, spawn.immutable(), traits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,6 +28,13 @@ public final class AgeRegistrySavedData extends SavedData {
|
|||||||
private static final String SPAWN_X_TAG = "SpawnX";
|
private static final String SPAWN_X_TAG = "SpawnX";
|
||||||
private static final String SPAWN_Y_TAG = "SpawnY";
|
private static final String SPAWN_Y_TAG = "SpawnY";
|
||||||
private static final String SPAWN_Z_TAG = "SpawnZ";
|
private static final String SPAWN_Z_TAG = "SpawnZ";
|
||||||
|
private static final String TRAITS_TAG = "Traits";
|
||||||
|
private static final String BIOME_DISTRIBUTION_TAG = "BiomeDistribution";
|
||||||
|
private static final String LIGHTING_TAG = "Lighting";
|
||||||
|
private static final String WEATHER_TAG = "Weather";
|
||||||
|
private static final String CELESTIAL_PERIOD_TAG = "CelestialPeriod";
|
||||||
|
private static final String CELESTIAL_ANGLE_TAG = "CelestialAngle";
|
||||||
|
private static final String CELESTIAL_PHASE_TAG = "CelestialPhase";
|
||||||
private static final Factory<AgeRegistrySavedData> FACTORY = new Factory<>(
|
private static final Factory<AgeRegistrySavedData> FACTORY = new Factory<>(
|
||||||
AgeRegistrySavedData::new,
|
AgeRegistrySavedData::new,
|
||||||
AgeRegistrySavedData::load,
|
AgeRegistrySavedData::load,
|
||||||
@@ -88,7 +95,8 @@ public final class AgeRegistrySavedData extends SavedData {
|
|||||||
AgeDefinition.CURRENT_GENERATION_VERSION,
|
AgeDefinition.CURRENT_GENERATION_VERSION,
|
||||||
createdGameTime,
|
createdGameTime,
|
||||||
dimensionId,
|
dimensionId,
|
||||||
null
|
null,
|
||||||
|
AgeTraits.select(seed)
|
||||||
);
|
);
|
||||||
ages.put(created.id(), created);
|
ages.put(created.id(), created);
|
||||||
setDirty();
|
setDirty();
|
||||||
@@ -123,7 +131,8 @@ public final class AgeRegistrySavedData extends SavedData {
|
|||||||
entry.contains(VERSION_TAG) ? entry.getInt(VERSION_TAG) : 1,
|
entry.contains(VERSION_TAG) ? entry.getInt(VERSION_TAG) : 1,
|
||||||
entry.getLong(CREATED_TIME_TAG),
|
entry.getLong(CREATED_TIME_TAG),
|
||||||
dimensionId,
|
dimensionId,
|
||||||
spawn
|
spawn,
|
||||||
|
readTraits(entry)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
@@ -148,12 +157,40 @@ public final class AgeRegistrySavedData extends SavedData {
|
|||||||
entry.putInt(SPAWN_Y_TAG, age.spawn().getY());
|
entry.putInt(SPAWN_Y_TAG, age.spawn().getY());
|
||||||
entry.putInt(SPAWN_Z_TAG, age.spawn().getZ());
|
entry.putInt(SPAWN_Z_TAG, age.spawn().getZ());
|
||||||
}
|
}
|
||||||
|
CompoundTag traits = new CompoundTag();
|
||||||
|
traits.putString(BIOME_DISTRIBUTION_TAG, age.traits().biomeDistribution().name());
|
||||||
|
traits.putString(LIGHTING_TAG, age.traits().lighting().name());
|
||||||
|
traits.putString(WEATHER_TAG, age.traits().weather().name());
|
||||||
|
traits.putDouble(CELESTIAL_PERIOD_TAG, age.traits().celestialPeriod());
|
||||||
|
traits.putFloat(CELESTIAL_ANGLE_TAG, age.traits().celestialAngle());
|
||||||
|
traits.putFloat(CELESTIAL_PHASE_TAG, age.traits().celestialPhase());
|
||||||
|
entry.put(TRAITS_TAG, traits);
|
||||||
serializedAges.add(entry);
|
serializedAges.add(entry);
|
||||||
});
|
});
|
||||||
tag.put(AGES_TAG, serializedAges);
|
tag.put(AGES_TAG, serializedAges);
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static AgeTraits readTraits(CompoundTag age) {
|
||||||
|
if (!age.contains(TRAITS_TAG, CompoundTag.TAG_COMPOUND)) {
|
||||||
|
return AgeTraits.LEGACY_DEFAULTS;
|
||||||
|
}
|
||||||
|
CompoundTag traits = age.getCompound(TRAITS_TAG);
|
||||||
|
AgeTraits defaults = AgeTraits.LEGACY_DEFAULTS;
|
||||||
|
try {
|
||||||
|
return new AgeTraits(
|
||||||
|
AgeTraits.parseOrDefault(traits.getString(BIOME_DISTRIBUTION_TAG), defaults.biomeDistribution()),
|
||||||
|
AgeTraits.parseOrDefault(traits.getString(LIGHTING_TAG), defaults.lighting()),
|
||||||
|
AgeTraits.parseOrDefault(traits.getString(WEATHER_TAG), defaults.weather()),
|
||||||
|
traits.contains(CELESTIAL_PERIOD_TAG) ? traits.getDouble(CELESTIAL_PERIOD_TAG) : defaults.celestialPeriod(),
|
||||||
|
traits.contains(CELESTIAL_ANGLE_TAG) ? traits.getFloat(CELESTIAL_ANGLE_TAG) : defaults.celestialAngle(),
|
||||||
|
traits.contains(CELESTIAL_PHASE_TAG) ? traits.getFloat(CELESTIAL_PHASE_TAG) : defaults.celestialPhase()
|
||||||
|
);
|
||||||
|
} catch (IllegalArgumentException exception) {
|
||||||
|
return defaults;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String normalizedName(String name) {
|
private static String normalizedName(String name) {
|
||||||
String normalized = name.strip();
|
String normalized = name.strip();
|
||||||
return normalized.isEmpty() ? "Unnamed Age" : normalized;
|
return normalized.isEmpty() ? "Unnamed Age" : normalized;
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package net.dnorberg.rivencraft.book;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/** Immutable random-Age traits selected once from the Age seed. */
|
||||||
|
public record AgeTraits(
|
||||||
|
BiomeDistribution biomeDistribution,
|
||||||
|
Lighting lighting,
|
||||||
|
Weather weather,
|
||||||
|
double celestialPeriod,
|
||||||
|
float celestialAngle,
|
||||||
|
float celestialPhase
|
||||||
|
) {
|
||||||
|
public static final double MIN_CELESTIAL_PERIOD = 0.2D;
|
||||||
|
public static final double MAX_CELESTIAL_PERIOD = 2.0D;
|
||||||
|
public static final float MIN_CELESTIAL_ANGLE = 0.0F;
|
||||||
|
public static final float MAX_CELESTIAL_ANGLE = 360.0F;
|
||||||
|
public static final float MIN_CELESTIAL_PHASE = 0.0F;
|
||||||
|
public static final float MAX_CELESTIAL_PHASE = 1.0F;
|
||||||
|
|
||||||
|
public static final AgeTraits LEGACY_DEFAULTS = new AgeTraits(
|
||||||
|
BiomeDistribution.NATIVE, Lighting.NORMAL, Weather.NORMAL,
|
||||||
|
1.0D, 0.0F, 0.0F
|
||||||
|
);
|
||||||
|
|
||||||
|
public AgeTraits {
|
||||||
|
if (biomeDistribution == null || lighting == null || weather == null) {
|
||||||
|
throw new IllegalArgumentException("Age trait choices cannot be null");
|
||||||
|
}
|
||||||
|
if (celestialPeriod < MIN_CELESTIAL_PERIOD || celestialPeriod > MAX_CELESTIAL_PERIOD) {
|
||||||
|
throw new IllegalArgumentException("Celestial period is outside the legacy range");
|
||||||
|
}
|
||||||
|
if (celestialAngle < MIN_CELESTIAL_ANGLE || celestialAngle >= MAX_CELESTIAL_ANGLE) {
|
||||||
|
throw new IllegalArgumentException("Celestial angle is outside the legacy range");
|
||||||
|
}
|
||||||
|
if (celestialPhase < MIN_CELESTIAL_PHASE || celestialPhase >= MAX_CELESTIAL_PHASE) {
|
||||||
|
throw new IllegalArgumentException("Celestial phase is outside the legacy range");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AgeTraits select(long seed) {
|
||||||
|
Random random = new Random(seed ^ 0x524956454E435241L);
|
||||||
|
return new AgeTraits(
|
||||||
|
randomChoice(random, BiomeDistribution.values()),
|
||||||
|
randomChoice(random, Lighting.values()),
|
||||||
|
randomChoice(random, Weather.values()),
|
||||||
|
MIN_CELESTIAL_PERIOD
|
||||||
|
+ random.nextDouble() * (MAX_CELESTIAL_PERIOD - MIN_CELESTIAL_PERIOD),
|
||||||
|
random.nextFloat() * MAX_CELESTIAL_ANGLE,
|
||||||
|
random.nextFloat()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <E extends Enum<E>> E parseOrDefault(String value, E fallback) {
|
||||||
|
try {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Class<E> type = (Class<E>) fallback.getDeclaringClass();
|
||||||
|
return Enum.valueOf(type, value);
|
||||||
|
} catch (IllegalArgumentException exception) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <E> E randomChoice(Random random, E[] values) {
|
||||||
|
return values[random.nextInt(values.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BiomeDistribution {
|
||||||
|
NATIVE, SINGLE, TILED, GRID, TINY, SMALL, MEDIUM, LARGE, HUGE
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Lighting {
|
||||||
|
NORMAL, DARK, BRIGHT
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Weather {
|
||||||
|
NORMAL, FAST, SLOW, OFF, ALWAYS, CLOUDY, RAIN, SNOW, STORM
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import net.dnorberg.rivencraft.book.AgeRegistrySavedData;
|
|||||||
import net.dnorberg.rivencraft.book.LinkTarget;
|
import net.dnorberg.rivencraft.book.LinkTarget;
|
||||||
import net.dnorberg.rivencraft.book.LinkingService;
|
import net.dnorberg.rivencraft.book.LinkingService;
|
||||||
import net.dnorberg.rivencraft.book.AgeVisitSavedData;
|
import net.dnorberg.rivencraft.book.AgeVisitSavedData;
|
||||||
|
import net.dnorberg.rivencraft.book.AgeTraits;
|
||||||
import net.dnorberg.rivencraft.item.DescriptiveBookItem;
|
import net.dnorberg.rivencraft.item.DescriptiveBookItem;
|
||||||
import net.dnorberg.rivencraft.item.LinkingBookItem;
|
import net.dnorberg.rivencraft.item.LinkingBookItem;
|
||||||
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
||||||
@@ -118,6 +119,11 @@ public final class LinkingGameTests {
|
|||||||
"A page-free book must select a supported legacy-inspired terrain profile");
|
"A page-free book must select a supported legacy-inspired terrain profile");
|
||||||
helper.assertValueEqual(first.generationVersion(), AgeDefinition.CURRENT_GENERATION_VERSION,
|
helper.assertValueEqual(first.generationVersion(), AgeDefinition.CURRENT_GENERATION_VERSION,
|
||||||
"New Ages must record the current generation algorithm version");
|
"New Ages must record the current generation algorithm version");
|
||||||
|
helper.assertValueEqual(first.traits(), AgeTraits.select(first.seed()),
|
||||||
|
"A page-free book must persist the deterministic traits selected from its seed");
|
||||||
|
helper.assertTrue(first.traits().celestialPeriod() >= AgeTraits.MIN_CELESTIAL_PERIOD
|
||||||
|
&& first.traits().celestialPeriod() <= AgeTraits.MAX_CELESTIAL_PERIOD,
|
||||||
|
"The celestial period must remain inside Mystcraft Legacy's random range");
|
||||||
helper.assertTrue(first.seed() != another.seed(), "Distinct Age UUIDs must receive distinct random seeds");
|
helper.assertTrue(first.seed() != another.seed(), "Distinct Age UUIDs must receive distinct random seeds");
|
||||||
helper.succeed();
|
helper.succeed();
|
||||||
}
|
}
|
||||||
@@ -158,7 +164,8 @@ public final class LinkingGameTests {
|
|||||||
net.minecraft.resources.ResourceLocation.fromNamespaceAndPath(
|
net.minecraft.resources.ResourceLocation.fromNamespaceAndPath(
|
||||||
Rivencraft.MOD_ID, "gametest/" + profile.getPath() + "_" + id
|
Rivencraft.MOD_ID, "gametest/" + profile.getPath() + "_" + id
|
||||||
),
|
),
|
||||||
null
|
null,
|
||||||
|
AgeTraits.select(id.getMostSignificantBits())
|
||||||
);
|
);
|
||||||
var level = AgeDimensionService.ensureLoaded(helper.getLevel().getServer(), definition);
|
var level = AgeDimensionService.ensureLoaded(helper.getLevel().getServer(), definition);
|
||||||
helper.assertTrue(level != null, "The " + profile + " profile must load a runtime dimension");
|
helper.assertTrue(level != null, "The " + profile + " profile must load a runtime dimension");
|
||||||
|
|||||||
Reference in new issue
Block a user