diff --git a/src/main/java/fathertoast/specialmobs/common/config/Config.java b/src/main/java/fathertoast/specialmobs/common/config/Config.java index 523c913..ddb80f3 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/Config.java +++ b/src/main/java/fathertoast/specialmobs/common/config/Config.java @@ -24,6 +24,7 @@ public class Config { /** Performs initial loading of all configs in this mod. */ public static void initialize() { ToastConfigSpec.freezeFileWatcher = true; + ReadMeConfig.makeReadMe( CONFIG_DIR ); MAIN.SPEC.initialize(); MobFamily.initBestiary(); ToastConfigSpec.freezeFileWatcher = false; diff --git a/src/main/java/fathertoast/specialmobs/common/config/MainConfig.java b/src/main/java/fathertoast/specialmobs/common/config/MainConfig.java index e814bf1..7ce1524 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/MainConfig.java +++ b/src/main/java/fathertoast/specialmobs/common/config/MainConfig.java @@ -15,20 +15,6 @@ public class MainConfig extends Config.AbstractConfig { super( dir, fileName, "This config contains options that apply to the mod as a whole, including some master settings", "toggles for convenience." ); - SPEC.newLine(); - SPEC.comment( - "Terminology used in Special Mobs configs:", - " * Mob - An entity that is 'alive', short for \"Mobile\" or MobEntity.", - " * Family - The group of mobs based on (but not including) a particular vanilla mob; e.g., Creepers.", - " * Species - A specific type of mob within a family; e.g., Fire Creepers or vanilla-replacement Creepers.", - " * Vanilla Replacement - The one species within a family that is intended to be a replica of the base vanilla mob.", - " * Special Variant - Any species that is not the family's vanilla replacement. Includes species that are", - " replicas of 'vanilla special variants'; i.e. Husks and Strays.", - " * Mob Replacer - The tool that watches vanilla mob spawns and cancels them to spawn this mod's entities." ); - SPEC.newLine(); - SPEC.describeAttributeList(); - SPEC.newLine(); - SPEC.describeRegistryEntryList(); GENERAL = new General( SPEC ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/ReadMeConfig.java b/src/main/java/fathertoast/specialmobs/common/config/ReadMeConfig.java new file mode 100644 index 0000000..a9a40ab --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/ReadMeConfig.java @@ -0,0 +1,36 @@ +package fathertoast.specialmobs.common.config; + +import fathertoast.specialmobs.common.config.field.BooleanField; + +import java.io.File; + +public class ReadMeConfig extends Config.AbstractConfig { + @SuppressWarnings( "SameParameterValue" ) + static void makeReadMe( File dir ) { new ReadMeConfig( dir ).SPEC.initialize(); } + + /** Builds the config spec that should be used for this config. */ + private ReadMeConfig( File dir ) { + super( dir, "__README__", "This file contains helpful information about how to use the config files in this mod." ); + SPEC.newLine( 2 ); + SPEC.comment( + "Terminology used in Special Mobs configs:", + " * Mob - An entity that is 'alive', short for \"Mobile\" or MobEntity.", + " * Family - The group of mobs based on (but not including) a particular vanilla mob; e.g., Creepers.", + " * Species - A specific type of mob within a family; e.g., Fire Creepers or vanilla-replacement Creepers.", + " * Vanilla Replacement - The one species within a family that is intended to be a replica of the base vanilla mob.", + " * Special Variant - Any species that is not the family's vanilla replacement. Includes species that are", + " replicas of 'vanilla special variants'; i.e. Husks and Strays.", + " * Mob Replacer - The tool that watches vanilla mob spawns and cancels them to spawn this mod's entities." ); + SPEC.newLine( 2 ); + SPEC.describeAttributeList(); + SPEC.newLine( 2 ); + SPEC.describeRegistryEntryList(); + SPEC.newLine( 2 ); + SPEC.describeEnvironmentListPart1of2(); + SPEC.newLine(); + SPEC.describeEnvironmentListPart2of2(); + + SPEC.newLine( 4 ); + SPEC.define( new BooleanField( "secret_mode", false, (String[]) null ) ); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/family/FamilyConfig.java b/src/main/java/fathertoast/specialmobs/common/config/family/FamilyConfig.java index 95939bd..1d203a9 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/family/FamilyConfig.java +++ b/src/main/java/fathertoast/specialmobs/common/config/family/FamilyConfig.java @@ -4,10 +4,13 @@ import fathertoast.specialmobs.common.bestiary.MobFamily; import fathertoast.specialmobs.common.config.Config; import fathertoast.specialmobs.common.config.field.BooleanField; import fathertoast.specialmobs.common.config.field.DoubleField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; import fathertoast.specialmobs.common.config.field.IntField; import fathertoast.specialmobs.common.config.file.ToastConfigSpec; import fathertoast.specialmobs.common.config.file.TomlHelper; import fathertoast.specialmobs.common.config.util.ConfigUtil; +import fathertoast.specialmobs.common.config.util.EnvironmentEntry; +import fathertoast.specialmobs.common.config.util.EnvironmentList; import java.io.File; import java.util.List; @@ -51,7 +54,7 @@ public class FamilyConfig extends Config.AbstractConfig { public final DoubleField familyRandomScaling; - public final DoubleField specialVariantChance; + public final DoubleField.EnvironmentSensitive specialVariantChance; public final IntField[] specialVariantWeights; @@ -72,9 +75,17 @@ public class FamilyConfig extends Config.AbstractConfig { SPEC.newLine(); - specialVariantChance = SPEC.define( new DoubleField( "special_variant_chance", variantChance, DoubleField.Range.PERCENT, - "The chance for " + family.configName + " to spawn as special variants." ) ); - // TODO special variant chance exceptions + specialVariantChance = new DoubleField.EnvironmentSensitive( + SPEC.define( new DoubleField( "special_variant_chance.base", variantChance, DoubleField.Range.PERCENT, + "The chance for " + family.configName + " to spawn as special variants." ) ), + SPEC.define( new EnvironmentListField( "special_variant_chance.exceptions", new EnvironmentList( + EnvironmentEntry.builder( (float) variantChance * 0.5F ).beforeDays( 5 ).build(), // Also skips first night's full moon + EnvironmentEntry.builder( (float) variantChance * 2.0F ).atMaxMoonLight().aboveDifficulty( 0.5F ).build(), + EnvironmentEntry.builder( (float) variantChance * 1.5F ).atMaxMoonLight().build(), + EnvironmentEntry.builder( (float) variantChance * 1.5F ).aboveDifficulty( 0.5F ).build() ) + .setRange( DoubleField.Range.PERCENT ), + "The chance for " + family.configName + " to spawn as special variants when specific environmental conditions are met." ) ) + ); SPEC.newLine(); diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/DoubleField.java b/src/main/java/fathertoast/specialmobs/common/config/field/DoubleField.java index a3ecccd..c7a13d0 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/DoubleField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/DoubleField.java @@ -2,8 +2,12 @@ package fathertoast.specialmobs.common.config.field; import fathertoast.specialmobs.common.config.file.TomlHelper; import fathertoast.specialmobs.common.core.SpecialMobs; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Random; @@ -156,4 +160,89 @@ public class DoubleField extends AbstractConfigField { return getMin(); } } + + /** + * Represents a double field and an environment exception list, combined into one. + * This has convenience methods for returning the value that should be used based on the environment. + */ + public static class EnvironmentSensitive { + /** The base value. */ + private final DoubleField BASE; + /** The environment exceptions list. */ + private final EnvironmentListField EXCEPTIONS; + + /** Links two fields together as base and exceptions. */ + public EnvironmentSensitive( DoubleField base, EnvironmentListField exceptions ) { + BASE = base; + EXCEPTIONS = exceptions; + } + + /** @return Returns the config field's value. */ + public double get( World world, @Nullable BlockPos pos ) { return EXCEPTIONS.getOrElse( world, pos, BASE ); } + + /** @return Treats the config field's value as a percent chance (from 0 to 1) and returns the result of a single roll. */ + public boolean rollChance( Random random, World world, @Nullable BlockPos pos ) { return random.nextDouble() < get( world, pos ); } + } + + /** + * Represents an environment sensitive list of weighted values. Unlike the normal weighted list, this is just a simple + * wrapper class, and its weights are doubles. + * It sacrifices automation for flexibility, largely to help with the craziness of environment list fields. + */ + public static class EnvironmentSensitiveWeightedList { + + private final List> UNDERLYING_LIST; + + /** Links an array of values to two arrays of fields as base weights and exceptions. */ + public EnvironmentSensitiveWeightedList( T[] values, DoubleField[] baseWeights, EnvironmentListField[] weightExceptions ) { + if( values.length != baseWeights.length || values.length != weightExceptions.length ) + throw new IllegalArgumentException( "All arrays must be equal length!" ); + + final ArrayList> list = new ArrayList<>(); + for( int i = 0; i < values.length; i++ ) { + list.add( new Entry<>( values[i], new EnvironmentSensitive( baseWeights[i], weightExceptions[i] ) ) ); + + // Do a bit of error checking; allows us to ignore the possibility of negative weights + if( baseWeights[i].valueMin < 0.0 || weightExceptions[i].valueDefault.getMinValue() < 0.0 ) { + throw new IllegalArgumentException( "Weight is not allowed to be negative! See " + + baseWeights[i].getKey() + " and/or " + weightExceptions[i].getKey() ); + } + } + list.trimToSize(); + UNDERLYING_LIST = Collections.unmodifiableList( list ); + } + + /** @return Returns a random item from this weighted list. Null if none of the items have a positive weight. */ + @Nullable + public T next( Random random, World world, @Nullable BlockPos pos ) { + // Due to the 'nebulous' nature of environment-based weights, we must recalculate weights for EVERY call + final double[] weights = new double[UNDERLYING_LIST.size()]; + double targetWeight = 0.0; + for( int i = 0; i < weights.length; i++ ) { + targetWeight += weights[i] = UNDERLYING_LIST.get( i ).WEIGHT.get( world, pos ); + } + if( targetWeight <= 0.0 ) return null; + + // Now we unravel the target weight to a random point + targetWeight *= random.nextDouble(); + for( int i = 0; i < weights.length; i++ ) { + targetWeight -= weights[i]; + if( targetWeight < 0.0 ) return UNDERLYING_LIST.get( i ).VALUE; + } + + SpecialMobs.LOG.error( "Environment-sensitive weight list was unable to return a value when it should have! " + + "This is probably due to error in floating point calculations, perhaps try changing the scale of weights." ); + return null; + } + + private static class Entry { + final T VALUE; + final DoubleField.EnvironmentSensitive WEIGHT; + + Entry( T value, DoubleField.EnvironmentSensitive weight ) { + VALUE = value; + WEIGHT = weight; + } + } + } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java b/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java index a0acfcd..3481ea3 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java @@ -17,6 +17,7 @@ import net.minecraft.world.World; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; +import java.util.Locale; /** * Represents a config field with an environment list value. @@ -53,6 +54,7 @@ public class EnvironmentListField extends GenericField { public static final String ENV_DAY_TIME = "day_time"; public static final String ENV_TIME_FROM_MIDNIGHT = "time_from_midnight"; public static final String ENV_WORLD_TIME = "world_time"; + public static final String ENV_CHUNK_TIME = "chunk_time"; /** * Provides a description of how to use environment lists. Recommended to put at the top of any file using environment lists. @@ -78,7 +80,7 @@ public class EnvironmentListField extends GenericField { comment.add( " \"" + ENV_DIMENSION_PROPERTY + " (!)property\":" ); comment.add( " Valid property values: " + TomlHelper.literalList( (Object[]) DimensionPropertyEnvironment.Value.values() ) ); comment.add( " Dimension properties are the true/false values available to dimension types in data packs." ); - comment.add( " See the wiki for more info: [https://minecraft.fandom.com/wiki/Custom_dimension#Syntax]." ); + comment.add( " See the wiki for more info: [https://minecraft.fandom.com/wiki/Custom_dimension#Syntax]." ); comment.add( " \"" + ENV_DIMENSION_TYPE + " (!)namespace:dimension_type_name\":" ); comment.add( " The world's dimension type. In vanilla, these are only \"minecraft:overworld\", \"minecraft:the_nether\", or \"minecraft:the_end\"." ); // Biome-based @@ -89,7 +91,7 @@ public class EnvironmentListField extends GenericField { comment.add( " \"" + ENV_TEMPERATURE + " op value\" or \"" + ENV_TEMPERATURE + " (!)" + TemperatureEnvironment.FREEZING + "\":" ); comment.add( " Height-adjusted temperature. For reference, freezing is < 0.15 and hot is generally considered > 0.95." ); comment.add( " \"" + ENV_BIOME_CATEGORY + " (!)category\":" ); - comment.add( " Valid category values: " + TomlHelper.literalList( (Object[]) BiomeCategoryEnvironment.Value.values() ) ); + comment.add( " Valid category values: " + TomlHelper.literalList( (Object[]) BiomeCategory.values() ) ); comment.add( " \"" + ENV_BIOME + " (!)namespace:biome_name\":" ); comment.add( " The biome. See the wiki for vanilla biome names (resource locations) [https://minecraft.fandom.com/wiki/Biome#Biome_IDs]." ); // Position-based @@ -98,7 +100,7 @@ public class EnvironmentListField extends GenericField { comment.add( " \"" + ENV_Y + " op value\":" ); comment.add( " The y-value. For reference, sea level is normally 63 and lava level is normally 10." );//TODO change lava level to -54 for MC 1.18 comment.add( " \"" + ENV_Y_FROM_SEA + " op value\":" ); - comment.add( " The y-value from sea level. Expect the only air <= 0 to be in caves (which may still have direct view of the sky)." ); + comment.add( " The y-value from sea level. Expect the only air <= 0 to be in caves/ravines (which may still have direct view of the sky)." ); comment.add( " \"" + ENV_POSITION + " (!)state\":" ); comment.add( " Valid state values: " + TomlHelper.literalList( (Object[]) PositionEnvironment.Value.values() ) ); comment.add( " Miscellaneous conditions that generally do what you expect. For reference, 'near' a village is ~3 chunks, and redstone checks weak power." ); @@ -123,8 +125,11 @@ public class EnvironmentListField extends GenericField { comment.add( " The absolute time in ticks away from midnight. Value must be 0 to 12000." ); comment.add( " \"" + ENV_MOON_PHASE + " (!)phase\":" ); comment.add( " Valid phase values: " + TomlHelper.literalList( (Object[]) MoonPhaseEnvironment.Value.values() ) ); + comment.add( " For reference, the first day in a new world is always a full moon." ); comment.add( " \"" + ENV_WORLD_TIME + " op value\":" ); comment.add( " The total time the world has existed, in ticks. For reference, each day cycle is 24000 ticks and each lunar cycle is 192000 ticks." ); + comment.add( " \"" + ENV_CHUNK_TIME + " op value\":" ); + comment.add( " The total time the chunk has been loaded, in ticks. For reference, each day cycle is 24000 ticks and each lunar cycle is 192000 ticks." ); return comment; } @@ -167,7 +172,7 @@ public class EnvironmentListField extends GenericField { final List conditions = new ArrayList<>(); if( args.length > 1 ) { - final String[] condArgs = args[1].trim().split( "&" ); + final String[] condArgs = args[1].split( "&" ); for( String condArg : condArgs ) { conditions.add( parseCondition( condArg.trim(), line ) ); } @@ -216,7 +221,7 @@ public class EnvironmentListField extends GenericField { if( args.length < 2 ) value = ""; else value = args[1].trim(); - switch( args[0] ) { + switch( args[0].toLowerCase( Locale.ROOT ) ) { // Dimension-based case ENV_DIMENSION_PROPERTY: return new DimensionPropertyEnvironment( this, value ); @@ -259,6 +264,8 @@ public class EnvironmentListField extends GenericField { return new TimeFromMidnightEnvironment( this, value ); case ENV_WORLD_TIME: return new WorldTimeEnvironment( this, value ); + case ENV_CHUNK_TIME: + return new ChunkTimeEnvironment( this, value ); } // The environment name was not recognized; try to provide some good feedback because this field is complicated @@ -271,7 +278,7 @@ public class EnvironmentListField extends GenericField { ENV_STRUCTURE, ENV_Y, ENV_Y_FROM_SEA, ENV_POSITION, // Time-based ENV_DIFFICULTY, ENV_SPECIAL_DIFFICULTY, ENV_WEATHER, ENV_MOON_BRIGHTNESS, ENV_MOON_PHASE, ENV_DAY_TIME, - ENV_TIME_FROM_MIDNIGHT, ENV_WORLD_TIME + ENV_TIME_FROM_MIDNIGHT, ENV_WORLD_TIME, ENV_CHUNK_TIME }; final AbstractEnvironment fallback = new YEnvironment( ComparisonOperator.LESS_THAN, 0 ); SpecialMobs.LOG.warn( "Invalid environment '{}' for {} \"{}\"! Falling back to \"{}\". Environment name must be in the set [ {} ]. Invalid environment: {}", diff --git a/src/main/java/fathertoast/specialmobs/common/config/file/ToastConfigSpec.java b/src/main/java/fathertoast/specialmobs/common/config/file/ToastConfigSpec.java index dfd5ebc..0e313fe 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/file/ToastConfigSpec.java +++ b/src/main/java/fathertoast/specialmobs/common/config/file/ToastConfigSpec.java @@ -227,6 +227,30 @@ public class ToastConfigSpec { } } + /** Represents an appendix header comment. */ + private static class AppendixHeader extends Format { + /** The appendix comment. */ + private final List COMMENT; + + /** Create a new header action that will insert the opening file comment. */ + private AppendixHeader( List comment ) { + COMMENT = comment; + } + + /** Called when the config is saved. */ + @Override + public void write( ToastTomlWriter writer, CharacterOutput output ) { + writer.decreaseIndentLevel(); + writer.writeNewLine( output ); + writer.writeNewLine( output ); + + writer.writeComment( "Appendix:", output ); + writer.writeComment( COMMENT, output ); + + writer.increaseIndentLevel(); + } + } + /** Represents a category comment. */ private static class Category extends Format { /** The category comment. */ @@ -355,6 +379,9 @@ public class ToastConfigSpec { /** @param comment The file comment to insert. */ public void header( List comment ) { ACTIONS.add( new Header( this, comment ) ); } + /** @param comment The appendix comment to insert. */ + public void appendixHeader( String... comment ) { ACTIONS.add( new AppendixHeader( TomlHelper.newComment( comment ) ) ); } + /** Inserts a detailed description of how to use the registry entry list field. */ public void describeRegistryEntryList() { ACTIONS.add( new Comment( RegistryEntryListField.verboseDescription() ) ); } @@ -367,6 +394,12 @@ public class ToastConfigSpec { /** Inserts a detailed description of how to use the block list field. */ public void describeBlockList() { ACTIONS.add( new Comment( BlockListField.verboseDescription() ) ); } + /** Inserts the first part of a detailed description of how to use the environment list field. Should go with the other field descriptions. */ + public void describeEnvironmentListPart1of2() { ACTIONS.add( new Comment( EnvironmentListField.verboseDescription() ) ); } + + /** Inserts the second and last part of a detailed description of how to use the environment list field. Should go at the bottom of the file. */ + public void describeEnvironmentListPart2of2() { ACTIONS.add( new Comment( EnvironmentListField.environmentDescriptions() ) ); } + /** * @param name The category name. * @param comment The category comment to insert. diff --git a/src/main/java/fathertoast/specialmobs/common/config/file/ToastTomlWriter.java b/src/main/java/fathertoast/specialmobs/common/config/file/ToastTomlWriter.java index 4c3ab8a..fb9e442 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/file/ToastTomlWriter.java +++ b/src/main/java/fathertoast/specialmobs/common/config/file/ToastTomlWriter.java @@ -5,6 +5,7 @@ import com.electronwill.nightconfig.core.io.*; import fathertoast.specialmobs.common.config.field.AbstractConfigField; import fathertoast.specialmobs.common.core.SpecialMobs; +import javax.annotation.Nullable; import java.io.Writer; import java.util.Iterator; import java.util.List; @@ -85,7 +86,7 @@ public class ToastTomlWriter implements ConfigWriter { } /** Writes a literal array of single-line strings. */ - public void writeStringArray( List list, CharacterOutput output ) { + public void writeStringArray( @Nullable List list, CharacterOutput output ) { if( list == null || list.isEmpty() ) { writeLine( "[]", output ); return; diff --git a/src/main/java/fathertoast/specialmobs/common/config/species/SpeciesConfig.java b/src/main/java/fathertoast/specialmobs/common/config/species/SpeciesConfig.java index 1498cd0..108b22b 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/species/SpeciesConfig.java +++ b/src/main/java/fathertoast/specialmobs/common/config/species/SpeciesConfig.java @@ -38,8 +38,6 @@ public class SpeciesConfig extends Config.AbstractConfig { super( FamilyConfig.dir( species.family ), fileName( species ), String.format( "This config contains options that apply only to the %s %s species.", variantName( species ), ConfigUtil.camelCaseToLowerSpace( species.family.name ) ) ); - SPEC.newLine(); - SPEC.comment( "See the main mod config for common terms and how to use special field types like Attribute List or Registry List." ); speciesName = variantName( species ) + " " + species.family.configName; diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java index 6ea47e4..4b66e24 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java @@ -1,16 +1,20 @@ package fathertoast.specialmobs.common.config.util; import fathertoast.specialmobs.common.config.util.environment.*; -import fathertoast.specialmobs.common.config.util.environment.biome.BiomeEnvironment; +import fathertoast.specialmobs.common.config.util.environment.biome.*; +import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionPropertyEnvironment; import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionTypeEnvironment; +import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionTypeGroupEnvironment; +import fathertoast.specialmobs.common.config.util.environment.position.PositionEnvironment; import fathertoast.specialmobs.common.config.util.environment.position.StructureEnvironment; import fathertoast.specialmobs.common.config.util.environment.position.YEnvironment; import fathertoast.specialmobs.common.config.util.environment.position.YFromSeaEnvironment; +import fathertoast.specialmobs.common.config.util.environment.time.*; import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.DimensionType; import net.minecraft.world.World; -import net.minecraft.world.biome.Biome; import net.minecraft.world.gen.feature.structure.Structure; import javax.annotation.Nullable; @@ -64,10 +68,20 @@ public class EnvironmentEntry { return str.toString(); } + + // Builder Implementation + + /** Creates a new entry builder. The value is rounded to 2 decimal place precision. */ + public static Builder builder( float value ) { return new Builder( Math.round( value * 100.0 ) / 100.0 ); } + + /** Creates a new entry builder. */ public static Builder builder( double value ) { return new Builder( value ); } /** - * Builder class used to simplify creation of environment entries, with shortcuts for the most commonly used environments. + * Builder class used to simplify creation of environment entries for default configs, + * with shortcuts for the most commonly used environments. + *

+ * Keep in mind that ALL conditions in an entry must be satisfied for it to be chosen. */ public static class Builder { private final double VALUE; @@ -85,28 +99,187 @@ public class EnvironmentEntry { // Dimension-based - public Builder inDimensionType( RegistryKey dimType ) { return in( new DimensionTypeEnvironment( dimType ) ); } + /** Check if the dimension type causes water to instantly vaporize and has faster lava flow. */ + public Builder inUltraWarmDimension() { return inDimensionWithProperty( DimensionPropertyEnvironment.Value.ULTRAWARM, false ); } + + /** Check if the dimension type causes water to instantly vaporize and has faster lava flow. */ + public Builder notInUltraWarmDimension() { return inDimensionWithProperty( DimensionPropertyEnvironment.Value.ULTRAWARM, true ); } + + /** Check if the dimension type allows clocks, compasses, and beds to work. */ + public Builder inNaturalDimension() { return inDimensionWithProperty( DimensionPropertyEnvironment.Value.NATURAL, false ); } + + /** Check if the dimension type allows clocks, compasses, and beds to work. */ + public Builder notInNaturalDimension() { return inDimensionWithProperty( DimensionPropertyEnvironment.Value.NATURAL, true ); } + + private Builder inDimensionWithProperty( DimensionPropertyEnvironment.Value property, boolean invert ) { + return in( new DimensionPropertyEnvironment( property, invert ) ); + } + + public Builder inOverworld() { return inDimensionType( DimensionType.OVERWORLD_LOCATION, false ); } + + public Builder notInOverworld() { return inDimensionType( DimensionType.OVERWORLD_LOCATION, true ); } + + public Builder inNether() { return inDimensionType( DimensionType.NETHER_LOCATION, false ); } + + public Builder notInNether() { return inDimensionType( DimensionType.NETHER_LOCATION, true ); } + + public Builder inTheEnd() { return inDimensionType( DimensionType.END_LOCATION, false ); } + + public Builder notInTheEnd() { return inDimensionType( DimensionType.END_LOCATION, true ); } + + private Builder inDimensionType( RegistryKey dimType, boolean invert ) { return in( new DimensionTypeEnvironment( dimType, invert ) ); } + + /** Check if the dimension type is vanilla (registered with the "minecraft" namespace). */ + public Builder inVanillaDimension() { return in( new DimensionTypeGroupEnvironment( new ResourceLocation( "" ), false ) ); } + + /** Check if the dimension type is vanilla (registered with the "minecraft" namespace). */ + public Builder notInVanillaDimension() { return in( new DimensionTypeGroupEnvironment( new ResourceLocation( "" ), true ) ); } // Biome-based - public Builder inBiome( RegistryKey biome ) { return in( new BiomeEnvironment( biome ) ); } + /** Check if the biome has rain disabled. */ + public Builder inDryBiome() { return inAvgRainfall( ComparisonOperator.EQUAL_TO, 0.0F ); } + + /** Check if the biome has rain disabled. */ + public Builder notInDryBiome() { return inAvgRainfall( ComparisonOperator.EQUAL_TO.invert(), 0.0F ); } + + /** Check if the biome's humidity hinders fire spread. */ + public Builder inHumidBiome() { return inAvgRainfall( ComparisonOperator.GREATER_THAN, 0.85F ); } + + /** Check if the biome's humidity hinders fire spread. */ + public Builder notInHumidBiome() { return inAvgRainfall( ComparisonOperator.GREATER_THAN.invert(), 0.85F ); } + + private Builder inAvgRainfall( ComparisonOperator op, float value ) { return in( new RainfallEnvironment( op, value ) ); } + + /** Check if the temperature is freezing. */ + public Builder isFreezing() { return in( new TemperatureEnvironment( true ) ); } + + /** Check if the temperature is freezing. */ + public Builder isNotFreezing() { return in( new TemperatureEnvironment( false ) ); } + + /** Check if the temperature is warm (disables snow golem trails). */ + public Builder isWarm() { return isTemperature( ComparisonOperator.GREATER_OR_EQUAL, 0.8F ); } + + /** Check if the temperature is warm (disables snow golem trails). */ + public Builder isNotWarm() { return isTemperature( ComparisonOperator.GREATER_OR_EQUAL.invert(), 0.8F ); } + + /** Check if the temperature is hot (causes snow golems to die). */ + public Builder isHot() { return isTemperature( ComparisonOperator.GREATER_THAN, 1.0F ); } + + /** Check if the temperature is hot (causes snow golems to die). */ + public Builder isNotHot() { return isTemperature( ComparisonOperator.GREATER_THAN.invert(), 1.0F ); } + + private Builder isTemperature( ComparisonOperator op, float value ) { return in( new TemperatureEnvironment( op, value ) ); } + + /** Check if the biome belongs to a specific category. */ + public Builder inBiomeCategory( BiomeCategory category ) { return in( new BiomeCategoryEnvironment( category, false ) ); } + + /** Check if the biome belongs to a specific category. */ + public Builder notInBiomeCategory( BiomeCategory category ) { return in( new BiomeCategoryEnvironment( category, true ) ); } // Position-based - public Builder inStructure( Structure structure ) { return in( new StructureEnvironment( structure ) ); } + /** Check if the position is inside a particular structure. See {@link Structure}. */ + public Builder inStructure( Structure structure ) { return in( new StructureEnvironment( structure, false ) ); } - public Builder belowY( int y ) { return in( new YEnvironment( ComparisonOperator.LESS_THAN, y ) ); } + /** Check if the position is inside a particular structure. See {@link Structure}. */ + public Builder notInStructure( Structure structure ) { return in( new StructureEnvironment( structure, true ) ); } - public Builder atLeastY( int y ) { return in( new YEnvironment( ComparisonOperator.GREATER_OR_EQUAL, y ) ); } + /** Check if diamond/redstone ore can generate at the position. */ + public Builder belowDiamondLevel() { return belowY( 15 ); } // TODO update ore-based logic in 1.18 - public Builder belowSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_THAN, 0 ) ); } + /** Check if diamond/redstone ore can generate at the position. */ + public Builder aboveDiamondLevel() { return aboveY( 15 ); } - public Builder atLeastSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.GREATER_OR_EQUAL, 0 ) ); } + /** Check if gold/lapis ore can generate at the position. */ + public Builder belowGoldLevel() { return belowY( 33 ); } + + /** Check if gold/lapis ore can generate at the position. */ + public Builder aboveGoldLevel() { return aboveY( 33 ); } + + private Builder belowY( int y ) { return in( new YEnvironment( ComparisonOperator.LESS_OR_EQUAL, y ) ); } + + private Builder aboveY( int y ) { return in( new YEnvironment( ComparisonOperator.LESS_OR_EQUAL.invert(), y ) ); } + + /** Check if the position is above/below sea level. */ + public Builder belowSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_OR_EQUAL, 0 ) ); } + + /** Check if the position is above/below sea level. */ + public Builder aboveSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_OR_EQUAL.invert(), 0 ) ); } + + /** Check if the position is above/below the average sea floor. */ + public Builder belowSeaFloor() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_OR_EQUAL, -18 ) ); } + + /** Check if the position is above/below the average sea floor. */ + public Builder aboveSeaFloor() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_OR_EQUAL.invert(), -18 ) ); } + + public Builder canSeeSky() { return inPositionWithState( PositionEnvironment.Value.CAN_SEE_SKY, false ); } + + public Builder cannotSeeSky() { return inPositionWithState( PositionEnvironment.Value.CAN_SEE_SKY, true ); } + + public Builder isNearVillage() { return inPositionWithState( PositionEnvironment.Value.IS_NEAR_VILLAGE, false ); } + + public Builder isNotNearVillage() { return inPositionWithState( PositionEnvironment.Value.IS_NEAR_VILLAGE, true ); } + + public Builder isNearRaid() { return inPositionWithState( PositionEnvironment.Value.IS_NEAR_RAID, false ); } + + public Builder isNotNearRaid() { return inPositionWithState( PositionEnvironment.Value.IS_NEAR_RAID, true ); } + + private Builder inPositionWithState( PositionEnvironment.Value state, boolean invert ) { return in( new PositionEnvironment( state, invert ) ); } // Time-based + /** Check if the special difficulty multiplier is above a threshold (0 - 1). */ + public Builder aboveDifficulty( float percent ) { return in( new SpecialDifficultyEnvironment( ComparisonOperator.GREATER_OR_EQUAL, percent ) ); } + + /** Check if the special difficulty multiplier is above a threshold (0 - 1). */ + public Builder belowDifficulty( float percent ) { return in( new SpecialDifficultyEnvironment( ComparisonOperator.GREATER_OR_EQUAL.invert(), percent ) ); } + + public Builder isRaining() { return inWeather( WeatherEnvironment.Value.RAIN, false ); } // same as "is not clear" + + public Builder isNotRaining() { return inWeather( WeatherEnvironment.Value.RAIN, true ); } // same as "is clear" + + public Builder isThundering() { return inWeather( WeatherEnvironment.Value.THUNDER, false ); } + + public Builder isNotThundering() { return inWeather( WeatherEnvironment.Value.THUNDER, true ); } + + private Builder inWeather( WeatherEnvironment.Value weather, boolean invert ) { return in( new WeatherEnvironment( weather, invert ) ); } + + public Builder atMaxMoonLight() { return in( new MoonPhaseEnvironment( MoonPhaseEnvironment.Value.FULL, false ) ); } + + public Builder aboveHalfMoonLight() { return fromHalfMoonLight( ComparisonOperator.GREATER_THAN ); } + + public Builder atHalfMoonLight() { return fromHalfMoonLight( ComparisonOperator.EQUAL_TO ); } + + public Builder belowHalfMoonLight() { return fromHalfMoonLight( ComparisonOperator.LESS_THAN ); } + + public Builder atNoMoonLight() { return in( new MoonPhaseEnvironment( MoonPhaseEnvironment.Value.NEW, true ) ); } + + private Builder fromHalfMoonLight( ComparisonOperator op ) { return in( new MoonBrightnessEnvironment( op, 0.5F ) ); } + + public Builder isNight() { return in( new DayTimeEnvironment( DayTimeEnvironment.Value.NIGHT, false ) ); } + + public Builder isDay() { return in( new DayTimeEnvironment( DayTimeEnvironment.Value.DAY, false ) ); } + + /** Check if the time is during a quarter of the night centered on midnight. */ + public Builder isNearMidnight() { return in( new TimeFromMidnightEnvironment( ComparisonOperator.LESS_OR_EQUAL, 1_500 ) ); } + + /** Check if the time is during a quarter of the night centered on midnight. */ + public Builder isNotNearMidnight() { return in( new TimeFromMidnightEnvironment( ComparisonOperator.LESS_OR_EQUAL.invert(), 1_500 ) ); } + + /** Check if the world time is after a certain number of days. */ + public Builder afterDays( int days ) { return in( new WorldTimeEnvironment( ComparisonOperator.GREATER_OR_EQUAL, 24_000L * days ) ); } + + /** Check if the world time is after a certain number of days. */ + public Builder beforeDays( int days ) { return in( new WorldTimeEnvironment( ComparisonOperator.GREATER_OR_EQUAL.invert(), 24_000L * days ) ); } + + /** Check if the chunk inhabited time is after a certain number of days. */ + public Builder afterDaysInChunk( int days ) { return in( new ChunkTimeEnvironment( ComparisonOperator.GREATER_OR_EQUAL, 24_000L * days ) ); } + + /** Check if the chunk inhabited time is after a certain number of days. */ + public Builder beforeDaysInChunk( int days ) { return in( new ChunkTimeEnvironment( ComparisonOperator.GREATER_OR_EQUAL.invert(), 24_000L * days ) ); } } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/WeightedList.java b/src/main/java/fathertoast/specialmobs/common/config/util/WeightedList.java index ed91404..1c34862 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/WeightedList.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/WeightedList.java @@ -15,9 +15,6 @@ import java.util.*; */ @SuppressWarnings( "unused" ) public class WeightedList { - /** The spec used by this config that defines the file's format. */ - protected final ToastConfigSpec SPEC; - /** The weighted entries in this list. */ private final List> ENTRIES; /** The total weight of all entries in this list. */ @@ -26,20 +23,20 @@ public class WeightedList { /** * Creates a new weighted list config option and registers it and any needed definitions with the spec. */ - public WeightedList( ToastConfigSpec parent, String key, T[] values, String... description ) { - this( parent, key, Arrays.asList( values ), description ); + public WeightedList( ToastConfigSpec SPEC, String key, T[] values, @Nullable String... description ) { + this( SPEC, key, Arrays.asList( values ), description ); } /** * Creates a new weighted list config option and registers it and any needed definitions with the spec. */ - public WeightedList( ToastConfigSpec parent, String key, Iterable values, String... description ) { - SPEC = parent; - + public WeightedList( ToastConfigSpec SPEC, String key, Iterable values, @Nullable String... description ) { final IntField.Range fieldRange = IntField.Range.NON_NEGATIVE; - List comment = TomlHelper.newComment( description ); - comment.add( TomlHelper.multiFieldInfo( fieldRange ) ); - SPEC.comment( comment ); + if( description != null ) { + List comment = TomlHelper.newComment( description ); + comment.add( TomlHelper.multiFieldInfo( fieldRange ) ); + SPEC.comment( comment ); + } // Define each value's weight field and connect the value to its weight in an entry List> list = new ArrayList<>(); diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/ComparisonOperator.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/ComparisonOperator.java index 9744dc0..f960a90 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/ComparisonOperator.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/ComparisonOperator.java @@ -13,56 +13,54 @@ public enum ComparisonOperator { @Override public String toString() { return LITERAL; } + /** @return A convenience method that returns the opposite comparison operator only if the passed value is true. */ + public ComparisonOperator invert( boolean invert ) { return invert ? invert() : this; } + + /** @return The opposite comparison operator. */ + public ComparisonOperator invert() { + switch( this ) { + case LESS_THAN: return GREATER_OR_EQUAL; + case LESS_OR_EQUAL: return GREATER_THAN; + case GREATER_THAN: return LESS_OR_EQUAL; + case GREATER_OR_EQUAL: return LESS_THAN; + case EQUAL_TO: return NOT_EQUAL_TO; + case NOT_EQUAL_TO: return EQUAL_TO; + } + throw new IllegalStateException( "Inversion implementation is invalid! :(" ); + } + public boolean apply( float first, float second ) { switch( this ) { - case LESS_THAN: - return first < second; - case LESS_OR_EQUAL: - return first <= second; - case GREATER_THAN: - return first > second; - case GREATER_OR_EQUAL: - return first >= second; - case EQUAL_TO: - return first == second; - case NOT_EQUAL_TO: - return first != second; + case LESS_THAN: return first < second; + case LESS_OR_EQUAL: return first <= second; + case GREATER_THAN: return first > second; + case GREATER_OR_EQUAL: return first >= second; + case EQUAL_TO: return first == second; + case NOT_EQUAL_TO: return first != second; } throw new IllegalStateException( "Float comparison implementation is invalid! :(" ); } public boolean apply( int first, int second ) { switch( this ) { - case LESS_THAN: - return first < second; - case LESS_OR_EQUAL: - return first <= second; - case GREATER_THAN: - return first > second; - case GREATER_OR_EQUAL: - return first >= second; - case EQUAL_TO: - return first == second; - case NOT_EQUAL_TO: - return first != second; + case LESS_THAN: return first < second; + case LESS_OR_EQUAL: return first <= second; + case GREATER_THAN: return first > second; + case GREATER_OR_EQUAL: return first >= second; + case EQUAL_TO: return first == second; + case NOT_EQUAL_TO: return first != second; } throw new IllegalStateException( "Integer comparison implementation is invalid! :(" ); } public boolean apply( long first, long second ) { switch( this ) { - case LESS_THAN: - return first < second; - case LESS_OR_EQUAL: - return first <= second; - case GREATER_THAN: - return first > second; - case GREATER_OR_EQUAL: - return first >= second; - case EQUAL_TO: - return first == second; - case NOT_EQUAL_TO: - return first != second; + case LESS_THAN: return first < second; + case LESS_OR_EQUAL: return first <= second; + case GREATER_THAN: return first > second; + case GREATER_OR_EQUAL: return first >= second; + case EQUAL_TO: return first == second; + case NOT_EQUAL_TO: return first != second; } throw new IllegalStateException( "Long comparison implementation is invalid! :(" ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryEnvironment.java index 2b19374..f4e1bb4 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryEnvironment.java @@ -28,8 +28,6 @@ public abstract class DynamicRegistryEnvironment extends AbstractEnvironment /** The value of ConfigUtil#DYNAMIC_REGISTRY_VERSION at the time of last poll. */ private byte version = -1; - public DynamicRegistryEnvironment( ResourceLocation regKey ) { this( regKey, false ); } - public DynamicRegistryEnvironment( ResourceLocation regKey, boolean invert ) { FIELD = null; INVERT = invert; diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryGroupEnvironment.java index c39987d..b093904 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryGroupEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryGroupEnvironment.java @@ -31,8 +31,6 @@ public abstract class DynamicRegistryGroupEnvironment extends AbstractEnviron /** The value of ConfigUtil#DYNAMIC_REGISTRY_VERSION at the time of last poll. */ private byte version = -1; - public DynamicRegistryGroupEnvironment( ResourceLocation regKey ) { this( regKey, false ); } - public DynamicRegistryGroupEnvironment( ResourceLocation regKey, boolean invert ) { FIELD = null; INVERT = invert; diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/EnumEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/EnumEnvironment.java index df3ab48..71ec5c7 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/EnumEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/EnumEnvironment.java @@ -12,8 +12,6 @@ public abstract class EnumEnvironment> extends AbstractEnviron /** The enum value for this environment. */ protected final T VALUE; - public EnumEnvironment( T value ) { this( value, false ); } - public EnumEnvironment( T value, boolean invert ) { INVERT = invert; VALUE = value; diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryEnvironment.java index 96db0d1..a638d41 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryEnvironment.java @@ -22,8 +22,6 @@ public abstract class RegistryEnvironment> exte private T registryEntry; - public RegistryEnvironment( T regEntry ) { this( regEntry, false ); } - public RegistryEnvironment( T regEntry, boolean invert ) { FIELD = null; INVERT = invert; diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryGroupEnvironment.java index 6c436b1..ef9a71d 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryGroupEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryGroupEnvironment.java @@ -24,13 +24,15 @@ public abstract class RegistryGroupEnvironment> private List registryEntries; - public RegistryGroupEnvironment( T regEntry ) { this( regEntry, false ); } - public RegistryGroupEnvironment( T regEntry, boolean invert ) { + //noinspection ConstantConditions + this( regEntry.getRegistryName(), invert ); + } + + public RegistryGroupEnvironment( ResourceLocation regKey, boolean invert ) { FIELD = null; INVERT = invert; - //noinspection ConstantConditions - NAMESPACE = regEntry.getRegistryName().toString(); + NAMESPACE = regKey.toString(); } public RegistryGroupEnvironment( AbstractConfigField field, String line ) { diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategory.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategory.java new file mode 100644 index 0000000..91c92c5 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategory.java @@ -0,0 +1,31 @@ +package fathertoast.specialmobs.common.config.util.environment.biome; + +import net.minecraft.world.biome.Biome; + +/** + * Used to wrap the vanilla enum Biome.Category so that it can be safely used in configs. + * The declared names should match the string passed into vanilla enums' constructors so that both enums serialize identically. + */ +public enum BiomeCategory { + NONE( Biome.Category.NONE ), + TAIGA( Biome.Category.TAIGA ), + EXTREME_HILLS( Biome.Category.EXTREME_HILLS ), + JUNGLE( Biome.Category.JUNGLE ), + MESA( Biome.Category.MESA ), + PLAINS( Biome.Category.PLAINS ), + SAVANNA( Biome.Category.SAVANNA ), + ICY( Biome.Category.ICY ), + THE_END( Biome.Category.THEEND ), + BEACH( Biome.Category.BEACH ), + FOREST( Biome.Category.FOREST ), + OCEAN( Biome.Category.OCEAN ), + DESERT( Biome.Category.DESERT ), + RIVER( Biome.Category.RIVER ), + SWAMP( Biome.Category.SWAMP ), + MUSHROOM( Biome.Category.MUSHROOM ), + NETHER( Biome.Category.NETHER ); + + public final Biome.Category BASE; + + BiomeCategory( Biome.Category base ) { BASE = base; } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategoryEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategoryEnvironment.java index 21d7fc9..c065716 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategoryEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategoryEnvironment.java @@ -5,40 +5,14 @@ import fathertoast.specialmobs.common.config.field.EnvironmentListField; import fathertoast.specialmobs.common.config.util.environment.EnumEnvironment; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -import net.minecraft.world.biome.Biome; import javax.annotation.Nullable; -public class BiomeCategoryEnvironment extends EnumEnvironment { - public enum Value { - NONE( Biome.Category.NONE ), - TAIGA( Biome.Category.TAIGA ), - EXTREME_HILLS( Biome.Category.EXTREME_HILLS ), - JUNGLE( Biome.Category.JUNGLE ), - MESA( Biome.Category.MESA ), - PLAINS( Biome.Category.PLAINS ), - SAVANNA( Biome.Category.SAVANNA ), - ICY( Biome.Category.ICY ), - THE_END( Biome.Category.THEEND ), - BEACH( Biome.Category.BEACH ), - FOREST( Biome.Category.FOREST ), - OCEAN( Biome.Category.OCEAN ), - DESERT( Biome.Category.DESERT ), - RIVER( Biome.Category.RIVER ), - SWAMP( Biome.Category.SWAMP ), - MUSHROOM( Biome.Category.MUSHROOM ), - NETHER( Biome.Category.NETHER ); - - public final Biome.Category BASE; - - Value( Biome.Category vanillaCat ) { BASE = vanillaCat; } - } +public class BiomeCategoryEnvironment extends EnumEnvironment { - public BiomeCategoryEnvironment( Value value ) { super( value ); } + public BiomeCategoryEnvironment( BiomeCategory value, boolean invert ) { super( value, invert ); } - public BiomeCategoryEnvironment( Value value, boolean invert ) { super( value, invert ); } - - public BiomeCategoryEnvironment( AbstractConfigField field, String line ) { super( field, line, Value.values() ); } + public BiomeCategoryEnvironment( AbstractConfigField field, String line ) { super( field, line, BiomeCategory.values() ); } /** @return The string name of this environment, as it would appear in a config file. */ @Override diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeEnvironment.java index e2ce149..9e3c704 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeEnvironment.java @@ -13,8 +13,6 @@ import javax.annotation.Nullable; public class BiomeEnvironment extends DynamicRegistryEnvironment { - public BiomeEnvironment( RegistryKey biome ) { super( biome.getRegistryName() ); } - public BiomeEnvironment( RegistryKey biome, boolean invert ) { super( biome.getRegistryName(), invert ); } public BiomeEnvironment( AbstractConfigField field, String line ) { super( field, line ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeGroupEnvironment.java index 34c5398..ede1a0f 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeGroupEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeGroupEnvironment.java @@ -4,6 +4,7 @@ import fathertoast.specialmobs.common.config.field.AbstractConfigField; import fathertoast.specialmobs.common.config.field.EnvironmentListField; import fathertoast.specialmobs.common.config.util.environment.DynamicRegistryGroupEnvironment; import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.registry.Registry; import net.minecraft.world.biome.Biome; @@ -14,9 +15,9 @@ import java.util.List; public class BiomeGroupEnvironment extends DynamicRegistryGroupEnvironment { - public BiomeGroupEnvironment( RegistryKey biome ) { super( biome.getRegistryName() ); } + public BiomeGroupEnvironment( RegistryKey biome, boolean invert ) { this( biome.getRegistryName(), invert ); } - public BiomeGroupEnvironment( RegistryKey biome, boolean invert ) { super( biome.getRegistryName(), invert ); } + public BiomeGroupEnvironment( ResourceLocation regKey, boolean invert ) { super( regKey, invert ); } public BiomeGroupEnvironment( AbstractConfigField field, String line ) { super( field, line ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeTemperatureEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeTemperatureEnvironment.java index a49182d..f7a5312 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeTemperatureEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeTemperatureEnvironment.java @@ -2,33 +2,24 @@ package fathertoast.specialmobs.common.config.util.environment.biome; import fathertoast.specialmobs.common.config.field.AbstractConfigField; import fathertoast.specialmobs.common.config.field.EnvironmentListField; -import fathertoast.specialmobs.common.config.util.environment.CompareFloatEnvironment; import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import javax.annotation.Nullable; -public class BiomeTemperatureEnvironment extends CompareFloatEnvironment { +public class BiomeTemperatureEnvironment extends TemperatureEnvironment { + + public BiomeTemperatureEnvironment( boolean freezing ) { super( freezing ); } public BiomeTemperatureEnvironment( ComparisonOperator op, float value ) { super( op, value ); } - public BiomeTemperatureEnvironment( AbstractConfigField field, String line ) { super( field, TemperatureEnvironment.handleTempInput( line ) ); } + public BiomeTemperatureEnvironment( AbstractConfigField field, String line ) { super( field, line ); } /** @return The string name of this environment, as it would appear in a config file. */ @Override public String name() { return EnvironmentListField.ENV_BIOME_TEMPERATURE; } - /** @return The string value of this environment, as it would appear in a config file. */ - @Override - public String value() { - if( COMPARATOR == ComparisonOperator.LESS_THAN && VALUE == 0.15F ) - return TemperatureEnvironment.FREEZING; - if( COMPARATOR == ComparisonOperator.GREATER_THAN && VALUE == Math.nextDown( 0.15F ) ) - return "!" + TemperatureEnvironment.FREEZING; - return super.value(); - } - /** @return Returns the actual value to compare, or Float.NaN if there isn't enough information. */ @Override public float getActual( World world, @Nullable BlockPos pos ) { diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TemperatureEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TemperatureEnvironment.java index 9b90196..200dc3d 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TemperatureEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TemperatureEnvironment.java @@ -12,15 +12,20 @@ import javax.annotation.Nullable; public class TemperatureEnvironment extends CompareFloatEnvironment { public static final String FREEZING = "freezing"; + public static final float FREEZING_POINT = 0.15F; public static String handleTempInput( String line ) { if( line.equalsIgnoreCase( FREEZING ) ) - return ComparisonOperator.LESS_THAN + " " + 0.15F; + return ComparisonOperator.LESS_THAN + " " + FREEZING_POINT; if( line.equalsIgnoreCase( "!" + FREEZING ) ) - return ComparisonOperator.GREATER_THAN + " " + Math.nextDown( 0.15F ); + return ComparisonOperator.LESS_THAN.invert() + " " + FREEZING_POINT; return line; } + public TemperatureEnvironment( boolean freezing ) { + this( ComparisonOperator.LESS_THAN.invert( !freezing ), FREEZING_POINT ); + } + public TemperatureEnvironment( ComparisonOperator op, float value ) { super( op, value ); } public TemperatureEnvironment( AbstractConfigField field, String line ) { super( field, handleTempInput( line ) ); } @@ -32,10 +37,8 @@ public class TemperatureEnvironment extends CompareFloatEnvironment { /** @return The string value of this environment, as it would appear in a config file. */ @Override public String value() { - if( COMPARATOR == ComparisonOperator.LESS_THAN && VALUE == 0.15F ) - return FREEZING; - if( COMPARATOR == ComparisonOperator.GREATER_THAN && VALUE == Math.nextDown( 0.15F ) ) - return "!" + FREEZING; + if( COMPARATOR == ComparisonOperator.LESS_THAN && VALUE == FREEZING_POINT ) return FREEZING; + if( COMPARATOR == ComparisonOperator.LESS_THAN.invert() && VALUE == FREEZING_POINT ) return "!" + FREEZING; return super.value(); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionPropertyEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionPropertyEnvironment.java index ebd9a32..50f56e1 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionPropertyEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionPropertyEnvironment.java @@ -35,8 +35,6 @@ public class DimensionPropertyEnvironment extends EnumEnvironment { - public DimensionTypeEnvironment( RegistryKey dimType ) { super( dimType.getRegistryName() ); } - public DimensionTypeEnvironment( RegistryKey dimType, boolean invert ) { super( dimType.getRegistryName(), invert ); } public DimensionTypeEnvironment( AbstractConfigField field, String line ) { super( field, line ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeGroupEnvironment.java index 9af3e68..516dd03 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeGroupEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeGroupEnvironment.java @@ -4,6 +4,7 @@ import fathertoast.specialmobs.common.config.field.AbstractConfigField; import fathertoast.specialmobs.common.config.field.EnvironmentListField; import fathertoast.specialmobs.common.config.util.environment.DynamicRegistryGroupEnvironment; import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.registry.Registry; import net.minecraft.world.DimensionType; @@ -14,9 +15,9 @@ import java.util.List; public class DimensionTypeGroupEnvironment extends DynamicRegistryGroupEnvironment { - public DimensionTypeGroupEnvironment( RegistryKey dimType ) { super( dimType.getRegistryName() ); } + public DimensionTypeGroupEnvironment( RegistryKey dimType, boolean invert ) { this( dimType.getRegistryName(), invert ); } - public DimensionTypeGroupEnvironment( RegistryKey dimType, boolean invert ) { super( dimType.getRegistryName(), invert ); } + public DimensionTypeGroupEnvironment( ResourceLocation regKey, boolean invert ) { super( regKey, invert ); } public DimensionTypeGroupEnvironment( AbstractConfigField field, String line ) { super( field, line ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/PositionEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/PositionEnvironment.java index 71c3190..ca2c7a1 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/PositionEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/PositionEnvironment.java @@ -18,7 +18,7 @@ public class PositionEnvironment extends EnumEnvironment pos != null && world instanceof ServerWorld && ((ServerWorld) world).isVillage( pos ) ), IS_NEAR_VILLAGE( ( world, pos ) -> pos != null && world instanceof ServerWorld && ((ServerWorld) world).isCloseToVillage( pos, 3 ) ), - IS_RAIDED( ( world, pos ) -> pos != null && world instanceof ServerWorld && ((ServerWorld) world).isRaided( pos ) ), + IS_NEAR_RAID( ( world, pos ) -> pos != null && world instanceof ServerWorld && ((ServerWorld) world).isRaided( pos ) ), IS_IN_WATER( ( world, pos ) -> pos != null && world.getFluidState( pos ).is( FluidTags.WATER ) ), IS_IN_LAVA( ( world, pos ) -> pos != null && world.getFluidState( pos ).is( FluidTags.LAVA ) ), IS_IN_FLUID( ( world, pos ) -> pos != null && !world.getFluidState( pos ).isEmpty() ), @@ -31,8 +31,6 @@ public class PositionEnvironment extends EnumEnvironment> { - public StructureEnvironment( Structure structure ) { super( structure ); } - public StructureEnvironment( Structure structure, boolean invert ) { super( structure, invert ); } public StructureEnvironment( AbstractConfigField field, String line ) { super( field, line ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureGroupEnvironment.java index 584ee56..819cc12 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureGroupEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureGroupEnvironment.java @@ -3,6 +3,7 @@ package fathertoast.specialmobs.common.config.util.environment.position; import fathertoast.specialmobs.common.config.field.AbstractConfigField; import fathertoast.specialmobs.common.config.field.EnvironmentListField; import fathertoast.specialmobs.common.config.util.environment.RegistryGroupEnvironment; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.gen.feature.structure.Structure; @@ -16,9 +17,9 @@ import java.util.List; public class StructureGroupEnvironment extends RegistryGroupEnvironment> { - public StructureGroupEnvironment( Structure biome ) { super( biome ); } + public StructureGroupEnvironment( Structure structure, boolean invert ) { super( structure, invert ); } - public StructureGroupEnvironment( Structure biome, boolean invert ) { super( biome, invert ); } + public StructureGroupEnvironment( ResourceLocation regKey, boolean invert ) { super( regKey, invert ); } public StructureGroupEnvironment( AbstractConfigField field, String line ) { super( field, line ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/ChunkTimeEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/ChunkTimeEnvironment.java new file mode 100644 index 0000000..3be2629 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/ChunkTimeEnvironment.java @@ -0,0 +1,33 @@ +package fathertoast.specialmobs.common.config.util.environment.time; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.CompareLongEnvironment; +import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class ChunkTimeEnvironment extends CompareLongEnvironment { + + public ChunkTimeEnvironment( ComparisonOperator op, long value ) { super( op, value ); } + + public ChunkTimeEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The minimum value that can be given to the value. */ + @Override + protected long getMinValue() { return 0L; } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_CHUNK_TIME; } + + /** @return Returns the actual value to compare, or null if there isn't enough information. */ + @Override + public Long getActual( World world, @Nullable BlockPos pos ) { + // Ignore deprecation; this is intentionally the same method used by World#getCurrentDifficultyAt + //noinspection deprecation + return pos == null || !world.hasChunkAt( pos ) ? null : world.getChunkAt( pos ).getInhabitedTime(); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DayTimeEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DayTimeEnvironment.java index 98f038d..1a9a9b9 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DayTimeEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DayTimeEnvironment.java @@ -27,8 +27,6 @@ public class DayTimeEnvironment extends EnumEnvironment mobFamily, World world, BlockPos entityPos ) { - return world.random.nextFloat() < mobFamily.config.GENERAL.specialVariantChance.get(); //TODO environment exceptions + return world.random.nextDouble() < mobFamily.config.GENERAL.specialVariantChance.get( world, entityPos ); } /** @return True if a mob should be replaced. */