okie I will use these tomorrow

This commit is contained in:
FatherToast 2022-07-17 02:01:55 -05:00
parent c16dd2c61e
commit ab7002c22c
71 changed files with 2256 additions and 107 deletions

View file

@ -13,6 +13,7 @@ import net.minecraft.potion.Effects;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.ForgeRegistries;
import javax.annotation.Nullable;
import java.util.*;
/**
@ -280,13 +281,13 @@ public class BestiaryInfo {
public Builder vanillaTextureBaseOnly( String tex ) { return vanillaBaseTexture( tex ).noEyesTexture().noOverlayTexture(); }
/** Sets the species default base texture. */
private Builder vanillaBaseTexture( String tex ) { return baseTexture( tex == null ? null : new ResourceLocation( tex ) ); }
private Builder vanillaBaseTexture( String tex ) { return baseTexture( new ResourceLocation( tex ) ); }
/** Sets the species default glowing eyes texture. */
private Builder vanillaEyesTexture( String eyeTex ) { return eyesTexture( eyeTex == null ? null : new ResourceLocation( eyeTex ) ); }
private Builder vanillaEyesTexture( String eyeTex ) { return eyesTexture( new ResourceLocation( eyeTex ) ); }
/** Sets the species default overlay texture. */
private Builder vanillaOverlayTexture( String ovrTex ) { return overlayTexture( ovrTex == null ? null : new ResourceLocation( ovrTex ) ); }
private Builder vanillaOverlayTexture( String ovrTex ) { return overlayTexture( new ResourceLocation( ovrTex ) ); }
//--------------- Textures (Auto-selected) ----------------
@ -351,19 +352,19 @@ public class BestiaryInfo {
public Builder noAnimationTexture() { return noOverlayTexture(); }
/** Sets the species default base texture. */
private Builder baseTexture( ResourceLocation tex ) {
private Builder baseTexture( @Nullable ResourceLocation tex ) {
texture = tex;
return this;
}
/** Sets the species default glowing eyes texture. */
private Builder eyesTexture( ResourceLocation eyeTex ) {
private Builder eyesTexture( @Nullable ResourceLocation eyeTex ) {
eyesTexture = eyeTex;
return this;
}
/** Sets the species default overlay texture. */
private Builder overlayTexture( ResourceLocation ovrTex ) {
private Builder overlayTexture( @Nullable ResourceLocation ovrTex ) {
overlayTexture = ovrTex;
return this;
}

View file

@ -6,7 +6,6 @@ import fathertoast.specialmobs.common.core.register.SMEntities;
import fathertoast.specialmobs.common.core.register.SMItems;
import fathertoast.specialmobs.common.util.AnnotationHelper;
import fathertoast.specialmobs.common.util.References;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
@ -18,7 +17,6 @@ import net.minecraftforge.common.ForgeSpawnEggItem;
import net.minecraftforge.fml.RegistryObject;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.*;
import java.util.function.Function;
@ -28,8 +26,6 @@ import java.util.function.Function;
*
* @see MobFamily.Species
*/
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
public class MobFamily<T extends LivingEntity, V extends FamilyConfig> {
/** List of all families, generated to make iteration possible. */
private static final ArrayList<MobFamily<?, ?>> FAMILY_LIST = new ArrayList<>();
@ -238,8 +234,6 @@ public class MobFamily<T extends LivingEntity, V extends FamilyConfig> {
*
* @see MobFamily
*/
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
public static class Species<T extends LivingEntity> {
/** The special mob family this species belongs to. */
public final MobFamily<? super T, ?> family;

View file

@ -0,0 +1,7 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package fathertoast.specialmobs.common.bestiary;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -0,0 +1,7 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package fathertoast.specialmobs.common.config.family;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -25,7 +25,7 @@ public abstract class AbstractConfigField {
* Creates a new field with the supplied key and description.
* If the description is null, it will cancel the entire comment, including the automatic field info text.
*/
protected AbstractConfigField( String key, String... description ) {
protected AbstractConfigField( String key, @Nullable String... description ) {
this( loadingCategory + key, description == null ? null : TomlHelper.newComment( description ) );
}
@ -33,7 +33,7 @@ public abstract class AbstractConfigField {
* Creates a new field with the supplied key and comment. This method is only used for very special circumstances.
* If the comment is null, it will cancel the entire comment, including the automatic field info text.
*/
AbstractConfigField( String key, List<String> comment ) {
AbstractConfigField( String key, @Nullable List<String> comment ) {
KEY = key;
COMMENT = comment;
}

View file

@ -42,16 +42,10 @@ public class AttributeListField extends GenericField<AttributeList> {
public ConfigDrivenAttributeModifierMap linkedAttributeMap;
/** Creates a new field. */
public AttributeListField( String key, AttributeList defaultValue, String... description ) {
public AttributeListField( String key, AttributeList defaultValue, @Nullable String... description ) {
super( key, defaultValue, description );
}
/** Applies all attribute changes in this list to the entity attribute builder. */
public void apply( AttributeModifierMap.MutableAttribute builder ) { get().apply( builder ); }
/** Applies all attribute changes in this list to the entity. */
public void apply( LivingEntity entity ) { get().apply( entity ); }
/** Adds info about the field type, format, and bounds to the end of a field's description. */
public void appendFieldInfo( List<String> comment ) {
comment.add( TomlHelper.fieldInfoFormat( "Attribute List", valueDefault,
@ -81,7 +75,7 @@ public class AttributeListField extends GenericField<AttributeList> {
if( linkedAttributeMap != null ) linkedAttributeMap.invalidate();
}
/** Parses a single entry line and returns a valid result if possible, or null if the entry is completely invalid. */
/** Parses a single entry line and returns a valid result. */
private AttributeEntry parseEntry( final String line ) {
// Parse the attribute-operation-value array
final String[] args = line.split( " ", 4 );
@ -148,4 +142,13 @@ public class AttributeListField extends GenericField<AttributeList> {
}
return value;
}
// Convenience methods
/** Applies all attribute changes in this list to the entity attribute builder. */
public void apply( AttributeModifierMap.MutableAttribute builder ) { get().apply( builder ); }
/** Applies all attribute changes in this list to the entity. */
public void apply( LivingEntity entity ) { get().apply( entity ); }
}

View file

@ -28,7 +28,7 @@ public class BlockListField extends GenericField<BlockList> {
}
/** Creates a new field. */
public BlockListField( String key, BlockList defaultValue, String... description ) {
public BlockListField( String key, BlockList defaultValue, @Nullable String... description ) {
super( key, defaultValue, description );
}
@ -53,6 +53,15 @@ public class BlockListField extends GenericField<BlockList> {
value = new BlockList( this, TomlHelper.parseStringList( raw ) );
}
// Convenience methods
/** @return Returns true if there are no entries in this block list. */
public boolean isEmpty() { return get().isEmpty(); }
/** @return Returns true if the block is contained in this list. */
public boolean matches( BlockState blockState ) { return get().matches( blockState ); }
/**
* Represents two block list fields, a blacklist and a whitelist, combined into one.
*/
@ -73,7 +82,7 @@ public class BlockListField extends GenericField<BlockList> {
/** @return Returns true if the block is contained in this list. */
public boolean matches( BlockState blockState ) {
return blockState != null && !BLACKLIST.get().matches( blockState ) && WHITELIST.get().matches( blockState );
return !BLACKLIST.get().matches( blockState ) && WHITELIST.get().matches( blockState );
}
}
}

View file

@ -18,7 +18,7 @@ public class BooleanField extends AbstractConfigField {
private boolean value;
/** Creates a new field. */
public BooleanField( String key, boolean defaultValue, String... description ) {
public BooleanField( String key, boolean defaultValue, @Nullable String... description ) {
super( key, description );
valueDefault = defaultValue;
}

View file

@ -23,12 +23,12 @@ public class DoubleField extends AbstractConfigField {
private double value;
/** Creates a new field that accepts a common range of values. */
public DoubleField( String key, double defaultValue, Range range, String... description ) {
public DoubleField( String key, double defaultValue, Range range, @Nullable String... description ) {
this( key, defaultValue, range.MIN, range.MAX, description );
}
/** Creates a new field that accepts a specialized range of values. */
public DoubleField( String key, double defaultValue, double min, double max, String... description ) {
public DoubleField( String key, double defaultValue, double min, double max, @Nullable String... description ) {
super( key, description );
valueDefault = defaultValue;
valueMin = min;

View file

@ -33,7 +33,7 @@ public class EntityListField extends GenericField<EntityList> {
}
/** Creates a new field. */
public EntityListField( String key, EntityList defaultValue, String... description ) {
public EntityListField( String key, EntityList defaultValue, @Nullable String... description ) {
super( key, defaultValue, description );
}
@ -191,6 +191,36 @@ public class EntityListField extends GenericField<EntityList> {
return value;
}
// Convenience methods
/** @return True if the entity is contained in this list. */
public boolean contains( @Nullable Entity entity ) { return get().contains( entity ); }
/**
* @param entity The entity to retrieve values for.
* @return The array of values of the best-match entry. Returns null if the entity is not contained in this entity list.
*/
@Nullable
public double[] getValues( @Nullable Entity entity ) { return get().getValues( entity ); }
/**
* @param entity The entity to retrieve a value for.
* @return The first value in the best-match entry's value array. Returns 0 if the entity is not contained in this
* entity list or has no values specified. This should only be used for 'single value' lists.
* @see EntityList#setSingleValue()
* @see EntityList#setSinglePercent()
*/
public double getValue( @Nullable Entity entity ) { return get().getValue( entity ); }
/**
* @param entity The entity to roll a value for.
* @return Randomly rolls the first percentage value in the best-match entry's value array. Returns false if the entity
* is not contained in this entity list or has no values specified. This should only be used for 'single percent' lists.
* @see EntityList#setSinglePercent()
*/
public boolean rollChance( @Nullable LivingEntity entity ) { return get().rollChance( entity ); }
/**
* Represents two entity list fields, a blacklist and a whitelist, combined into one.
* The blacklist cannot contain values, but the whitelist can have any settings.
@ -210,17 +240,21 @@ public class EntityListField extends GenericField<EntityList> {
}
}
// Convenience methods
/** @return True if the entity is contained in this list. */
public boolean contains( Entity entity ) {
return entity != null && !BLACKLIST.get().contains( entity ) && WHITELIST.get().contains( entity );
public boolean contains( @Nullable Entity entity ) {
return entity != null && !BLACKLIST.contains( entity ) && WHITELIST.contains( entity );
}
/**
* @param entity The entity to retrieve values for.
* @return The array of values of the best-match entry. Returns null if the entity is not contained in this entity list.
*/
public double[] getValues( Entity entity ) {
return entity != null && !BLACKLIST.get().contains( entity ) ? WHITELIST.get().getValues( entity ) : null;
@Nullable
public double[] getValues( @Nullable Entity entity ) {
return entity != null && !BLACKLIST.contains( entity ) ? WHITELIST.getValues( entity ) : null;
}
/**
@ -230,8 +264,8 @@ public class EntityListField extends GenericField<EntityList> {
* @see EntityList#setSingleValue()
* @see EntityList#setSinglePercent()
*/
public double getValue( Entity entity ) {
return entity != null && !BLACKLIST.get().contains( entity ) ? WHITELIST.get().getValue( entity ) : 0.0;
public double getValue( @Nullable Entity entity ) {
return entity != null && !BLACKLIST.contains( entity ) ? WHITELIST.getValue( entity ) : 0.0;
}
/**
@ -240,8 +274,8 @@ public class EntityListField extends GenericField<EntityList> {
* is not contained in this entity list or has no values specified. This should only be used for 'single percent' lists.
* @see EntityList#setSinglePercent()
*/
public boolean rollChance( LivingEntity entity ) {
return entity != null && !BLACKLIST.get().contains( entity ) && WHITELIST.get().rollChance( entity );
public boolean rollChance( @Nullable LivingEntity entity ) {
return entity != null && !BLACKLIST.contains( entity ) && WHITELIST.rollChance( entity );
}
}
}

View file

@ -14,13 +14,13 @@ public class EnumField<T extends Enum<T>> extends GenericField<T> {
private final T[] valuesValid;
/** Creates a new field that accepts any enum value. */
public EnumField( String key, T defaultValue, String... description ) {
public EnumField( String key, T defaultValue, @Nullable String... description ) {
//noinspection unchecked
this( key, defaultValue, (T[]) defaultValue.getClass().getEnumConstants(), description );
}
/** Creates a new field that accepts the specified set of enum values. */
public EnumField( String key, T defaultValue, T[] validValues, String... description ) {
public EnumField( String key, T defaultValue, T[] validValues, @Nullable String... description ) {
super( key, defaultValue, description );
valuesValid = validValues;
}
@ -55,6 +55,7 @@ public class EnumField<T extends Enum<T>> extends GenericField<T> {
}
/** @return Attempts to parse the string literal as one of the valid values for this field and returns it, or null if invalid. */
@Nullable
private T parseValue( String name ) {
for( T val : valuesValid ) {
if( val.name().equalsIgnoreCase( name ) ) return val;

View file

@ -0,0 +1,291 @@
package fathertoast.specialmobs.common.config.field;
import fathertoast.specialmobs.common.config.file.TomlHelper;
import fathertoast.specialmobs.common.config.util.EnvironmentEntry;
import fathertoast.specialmobs.common.config.util.EnvironmentList;
import fathertoast.specialmobs.common.config.util.environment.*;
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.StructureEnvironment;
import fathertoast.specialmobs.common.config.util.environment.position.StructureGroupEnvironment;
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 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.List;
/**
* Represents a config field with an environment list value.
*/
public class EnvironmentListField extends GenericField<EnvironmentList> {
/* When adding new environment conditions, you must:
* - Add a new constant for its name here
* - Create the environment class and ensure #name() returns the new name constant
* - Link the name to construction in #parseCondition(String,String) below
* - Add any applicable builder methods in EnvironmentEntry.Builder
* - Describe the new environment condition's usage/format in #environmentDescriptions() below
*/
// Dimension-based
public static final String ENV_DIMENSION_PROPERTY = "dimension_property";
public static final String ENV_DIMENSION_TYPE = "dimension_type";
// Biome-based
public static final String ENV_RAINFALL = "rainfall";
public static final String ENV_BIOME_TEMPERATURE = "biome_temp";
public static final String ENV_TEMPERATURE = "temp";
public static final String ENV_BIOME_CATEGORY = "biome_category";
public static final String ENV_BIOME = "biome";
// Position-based TODO can_see_sky
public static final String ENV_STRUCTURE = "structure";
public static final String ENV_Y = "y";
public static final String ENV_Y_FROM_SEA = "y_from_sea";
// Time-based
public static final String ENV_DIFFICULTY = "difficulty";
public static final String ENV_SPECIAL_DIFFICULTY = "special_difficulty";
public static final String ENV_WEATHER = "weather";
public static final String ENV_MOON_BRIGHTNESS = "moon_brightness";
public static final String ENV_MOON_PHASE = "moon_phase";
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";
/**
* Provides a description of how to use environment lists. Recommended to put at the top of any file using environment lists.
* Always use put the environment condition descriptions at the bottom of the file if this is used!
*/
public static List<String> verboseDescription() {
List<String> comment = new ArrayList<>();
comment.add( "Environment List fields: General format = [ \"value environment1 condition1 & environment2 condition2 & ...\", ... ]" );
comment.add( " Environment lists are arrays of environment entries. Each entry is a value followed by the environment conditions that must be" );
comment.add( " satisfied for the value to be chosen. The environments are tested in the order listed, and the first matching entry is chosen." );
comment.add( " See the bottom of this file for an explanation on each environment condition available." );
return comment;
}
/** Provides a detailed description of how to use each environment condition. Recommended to put at the bottom of any file using environment lists. */
public static List<String> environmentDescriptions() {
List<String> comment = new ArrayList<>();
comment.add( "Environment conditions (for Environment List entries):" );
comment.add( " Many environment conditions can be inverted by using \"!\"; these are shown with (!) in the appropriate location." );
comment.add( " Other environment conditions are numerical comparisons; these use the operators (shown as op) <, >, =, <=, >=, or != to compare value." );
comment.add( "Valid environment conditions are:" );
// Dimension-based
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( " \"" + 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
comment.add( " \"" + ENV_RAINFALL + " op value\":" );
comment.add( " Biome's rainfall parameter. If this is \"= 0\", it checks that rain is disabled. For reference, rainfall > 0.85 suppresses fire." );
comment.add( " \"" + ENV_BIOME_TEMPERATURE + " op value\" or \"" + ENV_BIOME_TEMPERATURE + " (!)" + TemperatureEnvironment.FREEZING + "\":" );
comment.add( " Biome's temperature parameter. For reference, freezing is < 0.15 and hot is generally considered > 0.95." );
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( " \"" + 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
comment.add( " \"" + ENV_STRUCTURE + " (!)namespace:structure_name\":" );
comment.add( " The structure. See the wiki for vanilla structure names [https://minecraft.fandom.com/wiki/Generated_structures#Locating]." );
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)." );
// Time-based
comment.add( " \"" + ENV_DIFFICULTY + " op value\":" );
comment.add( " The regional difficulty (0 to 6.75). This is based on many factors such as difficulty setting, moon brightness, chunk inhabited time, and world time." );
comment.add( " For reference, this scales up to the max after 63 days in the world and 150 days in a particular chunk, and peaks during full moons." );
comment.add( " On Peaceful this is always 0, on Easy this is 0.75 to 1.5, on Normal this is 1.5 to 4.0, and on Hard this is 2.25 to 6.75." );
comment.add( " \"" + ENV_SPECIAL_DIFFICULTY + " op value\":" );
comment.add( " The 'special multiplier' for regional difficulty (0 to 1). For reference, this is 0 when difficulty <= 2 and 1 when difficulty >= 4." );
comment.add( " This is always 0 in Easy and below. In Normal, it maxes at absolute peak regional difficulty. In Hard, it starts at 0.125 and maxes out in ~50 days." );
comment.add( " \"" + ENV_WEATHER + " (!)type\":" );
comment.add( " Valid type values: " + TomlHelper.literalList( (Object[]) WeatherEnvironment.Value.values() ) );
comment.add( " \"" + ENV_MOON_BRIGHTNESS + " op value\":" );
comment.add( " The moon brightness (0 to 1). New moon has 0 brightness, full moon has 1 brightness. Intermediate phases are 0.25, 0.5, or 0.75." );
comment.add( " \"" + ENV_MOON_PHASE + " (!)phase\":" );
comment.add( " Valid phase values: " + TomlHelper.literalList( (Object[]) MoonPhaseEnvironment.Value.values() ) );
comment.add( " \"" + ENV_DAY_TIME + " (!)time\":" );
comment.add( " Valid time values: " + TomlHelper.literalList( (Object[]) DayTimeEnvironment.Value.values() ) );
comment.add( " Note that the transition periods, sunset & sunrise, are considered as part of day & night, respectively." );
comment.add( " \"" + ENV_TIME_FROM_MIDNIGHT + " op value\":" );
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( " \"" + 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." );
return comment;
}
/** Creates a new field. */
public EnvironmentListField( String key, EnvironmentList defaultValue, String... description ) {
super( key, defaultValue, description );
}
/** Adds info about the field type, format, and bounds to the end of a field's description. */
public void appendFieldInfo( List<String> comment ) {
comment.add( TomlHelper.fieldInfoFormat( "Environment List", valueDefault, "[ \"value condition1 state1 & condition2 state2 & ...\", ... ]" ) );
comment.add( " Range for Values: " + TomlHelper.fieldRange( valueDefault.getMinValue(), valueDefault.getMaxValue() ) );
}
/**
* Loads this field's value from the given raw toml value. If anything goes wrong, correct it at the lowest level possible.
* <p>
* For example, a missing value should be set to the default, while an out-of-range value should be adjusted to the
* nearest in-range value
*/
@Override
public void load( @Nullable Object raw ) {
if( raw == null ) {
value = valueDefault;
return;
}
List<String> list = TomlHelper.parseStringList( raw );
List<EnvironmentEntry> entryList = new ArrayList<>();
for( String line : list ) {
entryList.add( parseEntry( line ) );
}
value = new EnvironmentList( entryList );
}
/** Parses a single entry line and returns the result. */
private EnvironmentEntry parseEntry( final String line ) {
// Parse the value out of the conditions
final String[] args = line.split( " ", 2 );
final double value = parseValue( args[0], line );
final List<AbstractEnvironment> conditions = new ArrayList<>();
if( args.length > 1 ) {
final String[] condArgs = args[1].trim().split( "&" );
for( String condArg : condArgs ) {
conditions.add( parseCondition( condArg.trim(), line ) );
}
}
if( conditions.isEmpty() ) {
SpecialMobs.LOG.warn( "No environments defined in entry for {} \"{}\"! Invalid entry: {}",
getClass(), getKey(), line );
}
return new EnvironmentEntry( value, conditions );
}
/** Parses a single value argument and returns a valid result. */
private double parseValue( final String arg, final String line ) {
// Try to parse the value
double value;
try {
value = Double.parseDouble( arg );
}
catch( NumberFormatException ex ) {
// This is thrown if the string is not a parsable number
SpecialMobs.LOG.warn( "Invalid value for {} \"{}\"! Falling back to 0. Invalid entry: {}",
getClass(), getKey(), line );
value = 0.0;
}
// Verify value is within range
if( value < valueDefault.getMinValue() ) {
SpecialMobs.LOG.warn( "Value for {} \"{}\" is below the minimum ({})! Clamping value. Invalid value: {}",
getClass(), getKey(), valueDefault.getMinValue(), value );
value = valueDefault.getMinValue();
}
else if( value > valueDefault.getMaxValue() ) {
SpecialMobs.LOG.warn( "Value for {} \"{}\" is above the maximum ({})! Clamping value. Invalid value: {}",
getClass(), getKey(), valueDefault.getMaxValue(), value );
value = valueDefault.getMaxValue();
}
return value;
}
/** Parses a single environment condition argument and returns a valid result. */
private AbstractEnvironment parseCondition( final String arg, final String line ) {
// First parse the environment name, since it defines the format for the rest
final String[] args = arg.split( " ", 2 );
final String value;
if( args.length < 2 ) value = "";
else value = args[1].trim();
switch( args[0] ) {
// Dimension-based
case ENV_DIMENSION_PROPERTY:
return new DimensionPropertyEnvironment( this, value );
case ENV_DIMENSION_TYPE:
return value.endsWith( "*" ) ? new DimensionTypeGroupEnvironment( this, value ) : new DimensionTypeEnvironment( this, value );
// Biome-based
case ENV_RAINFALL:
return new RainfallEnvironment( this, value );
case ENV_BIOME_TEMPERATURE:
return new BiomeTemperatureEnvironment( this, value );
case ENV_TEMPERATURE:
return new TemperatureEnvironment( this, value );
case ENV_BIOME_CATEGORY:
return new BiomeCategoryEnvironment( this, value );
case ENV_BIOME:
return value.endsWith( "*" ) ? new BiomeGroupEnvironment( this, value ) : new BiomeEnvironment( this, value );
// Position-based
case ENV_STRUCTURE:
return value.endsWith( "*" ) ? new StructureGroupEnvironment( this, value ) : new StructureEnvironment( this, value );
case ENV_Y:
return new YEnvironment( this, value );
case ENV_Y_FROM_SEA:
return new YFromSeaEnvironment( this, value );
// Time-based
case ENV_DIFFICULTY:
return new DifficultyEnvironment( this, value );
case ENV_SPECIAL_DIFFICULTY:
return new SpecialDifficultyEnvironment( this, value );
case ENV_WEATHER:
return new WeatherEnvironment( this, value );
case ENV_MOON_BRIGHTNESS:
return new MoonBrightnessEnvironment( this, value );
case ENV_MOON_PHASE:
return new MoonPhaseEnvironment( this, value );
case ENV_DAY_TIME:
return new DayTimeEnvironment( this, value );
case ENV_TIME_FROM_MIDNIGHT:
return new TimeFromMidnightEnvironment( this, value );
case ENV_WORLD_TIME:
return new WorldTimeEnvironment( this, value );
}
// The environment name was not recognized; try to provide some good feedback because this field is complicated
final String[] environmentNames = {
// Dimension-based
ENV_DIMENSION_PROPERTY, ENV_DIMENSION_TYPE,
// Biome-based
ENV_RAINFALL, ENV_BIOME_TEMPERATURE, ENV_TEMPERATURE, ENV_BIOME_CATEGORY, ENV_BIOME,
// Position-based
ENV_STRUCTURE, ENV_Y, ENV_Y_FROM_SEA,
// 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
};
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: {}",
args[0], getClass(), getKey(), fallback, TomlHelper.literalList( (Object[]) environmentNames ), line );
return fallback;
}
// Convenience methods
/** @return The value matching the given environment, or the default value if no matching environment is defined. */
public double getOrElse( World world, @Nullable BlockPos pos, DoubleField defaultValue ) { return get().getOrElse( world, pos, defaultValue ); }
/** @return The value matching the given environment, or the default value if no matching environment is defined. */
public double getOrElse( World world, @Nullable BlockPos pos, double defaultValue ) { return get().getOrElse( world, pos, defaultValue ); }
/** @return The value matching the given environment, or null if no matching environment is defined. */
@Nullable
public Double get( World world, @Nullable BlockPos pos ) { return get().get( world, pos ); }
}

View file

@ -1,5 +1,7 @@
package fathertoast.specialmobs.common.config.field;
import javax.annotation.Nullable;
/**
* Represents a config field with an object value.
* <p>
@ -16,7 +18,7 @@ public abstract class GenericField<T> extends AbstractConfigField {
protected T value;
/** Creates a new field. */
public GenericField( String key, T defaultValue, String... description ) {
public GenericField( String key, T defaultValue, @Nullable String... description ) {
super( key, description );
valueDefault = defaultValue;
}

View file

@ -23,12 +23,12 @@ public class IntField extends AbstractConfigField {
private int value;
/** Creates a new field that accepts a common range of values. */
public IntField( String key, int defaultValue, Range range, String... description ) {
public IntField( String key, int defaultValue, Range range, @Nullable String... description ) {
this( key, defaultValue, range.MIN, range.MAX, description );
}
/** Creates a new field that accepts a specialized range of values. */
public IntField( String key, int defaultValue, int min, int max, String... description ) {
public IntField( String key, int defaultValue, int min, int max, @Nullable String... description ) {
super( key, description );
valueDefault = defaultValue;
valueMin = min;

View file

@ -17,7 +17,7 @@ import javax.annotation.Nullable;
public class LazyRegistryEntryListField<T extends IForgeRegistryEntry<T>> extends RegistryEntryListField<T> {
/** Creates a new field. */
public LazyRegistryEntryListField( String key, RegistryEntryList<T> defaultValue, String... description ) {
public LazyRegistryEntryListField( String key, RegistryEntryList<T> defaultValue, @Nullable String... description ) {
super( key, defaultValue, description );
}

View file

@ -31,7 +31,7 @@ public class RegistryEntryListField<T extends IForgeRegistryEntry<T>> extends Ge
}
/** Creates a new field. */
public RegistryEntryListField( String key, RegistryEntryList<T> defaultValue, String... description ) {
public RegistryEntryListField( String key, RegistryEntryList<T> defaultValue, @Nullable String... description ) {
super( key, defaultValue, description );
}
@ -57,15 +57,18 @@ public class RegistryEntryListField<T extends IForgeRegistryEntry<T>> extends Ge
value = new RegistryEntryList<>( this, valueDefault.getRegistry(), TomlHelper.parseStringList( raw ) );
}
// Convenience methods
/** @return The registry this list draws from. */
public IForgeRegistry<T> getRegistry() { return value.getRegistry(); }
public IForgeRegistry<T> getRegistry() { return get().getRegistry(); }
/** @return The entries in this list. */
public Set<T> getEntries() { return value.getEntries(); }
public Set<T> getEntries() { return get().getEntries(); }
/** @return Returns true if there are no entries in this list. */
public boolean isEmpty() { return value.isEmpty(); }
public boolean isEmpty() { return get().isEmpty(); }
/** @return Returns true if the entry is contained in this list. */
public boolean contains( T entry ) { return value.contains( entry ); }
public boolean contains( @Nullable T entry ) { return get().contains( entry ); }
}

View file

@ -14,13 +14,13 @@ public class ScaledDoubleField extends DoubleField {
private double valueScaled;
/** Creates a new field that accepts a common range of values. */
public ScaledDoubleField( String key, double defaultValue, double scale, Range range, String... description ) {
public ScaledDoubleField( String key, double defaultValue, double scale, Range range, @Nullable String... description ) {
super( key, defaultValue, range, description );
SCALE = scale;
}
/** Creates a new field that accepts a specialized range of values. */
public ScaledDoubleField( String key, double defaultValue, double scale, double min, double max, String... description ) {
public ScaledDoubleField( String key, double defaultValue, double scale, double min, double max, @Nullable String... description ) {
super( key, defaultValue, min, max, description );
SCALE = scale;
}

View file

@ -11,12 +11,12 @@ public class SqrDoubleField extends DoubleField {
private double valueSqr;
/** Creates a new field that accepts a common range of values. */
public SqrDoubleField( String key, double defaultValue, Range range, String... description ) {
public SqrDoubleField( String key, double defaultValue, Range range, @Nullable String... description ) {
super( key, defaultValue, range, description );
}
/** Creates a new field that accepts a specialized range of values. */
public SqrDoubleField( String key, double defaultValue, double min, double max, String... description ) {
public SqrDoubleField( String key, double defaultValue, double min, double max, @Nullable String... description ) {
super( key, defaultValue, min, max, description );
}

View file

@ -0,0 +1,7 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package fathertoast.specialmobs.common.config.field;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -6,10 +6,12 @@ import fathertoast.specialmobs.common.config.field.DoubleField;
import fathertoast.specialmobs.common.config.field.IntField;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@SuppressWarnings( "unused" )
public final class TomlHelper {
private TomlHelper() { } // This is a static access only class that cannot be instantiated
@ -32,7 +34,7 @@ public final class TomlHelper {
}
/** Attempts to convert a string value to a raw non-string toml literal. May or may not be accurate. */
public static Object parseRaw( String value ) {
public static Object parseRaw( @Nullable String value ) {
// Note: It is very important here that the returned value is NOT a string
if( value != null && !"".equals( value ) ) {
@ -56,9 +58,9 @@ public final class TomlHelper {
}
/** Attempts to convert an object to a toml literal. May or may not be accurate. */
public static String toLiteral( Object value ) {
public static String toLiteral( @Nullable Object value ) {
if( value == null ) {
return "null";
return "";
}
else if( value instanceof Enum<?> ) {
return "\"" + ((Enum<?>) value).name().toLowerCase() + "\"";
@ -66,21 +68,22 @@ public final class TomlHelper {
else if( value instanceof String ) {
return "\"" + value + "\"";
}
else if( value instanceof Double && ((Double) value).isInfinite() ) {
// Toml infinite literals do not match java; these may also be unsupported in the current version
return (Double) value > 0.0 ? "Inf" : "-Inf";
}
// Infinite values not supported
//else if( value instanceof Double && ((Double) value).isInfinite() ) {
// // Toml infinite literals do not match java
// return (Double) value > 0.0 ? "Inf" : "-Inf";
//}
else {
return value.toString();
}
}
/** Attempts to convert an object array to a toml literal. May or may not be accurate. */
public static String toLiteral( Object... values ) {
public static String toLiteral( @Nullable Object... values ) {
if( values == null ) {
return "null";
return "";
}
else if( values.length < 1 ) {
if( values.length < 1 ) {
return "[]";
}
else {
@ -92,7 +95,7 @@ public final class TomlHelper {
public static String literalList( Object... list ) { return literalList( Arrays.asList( list ) ); }
/** Attempts to convert an object list to a list of toml literals. May or may not be accurate. */
public static String literalList( List<Object> list ) {
public static String literalList( @Nullable List<Object> list ) {
if( list == null || list.isEmpty() ) return "";
StringBuilder literals = new StringBuilder();
for( Object obj : list ) {
@ -196,7 +199,7 @@ public final class TomlHelper {
public static List<String> splitKey( String key ) { return StringUtils.split( key, '.' ); }
/** Combines a toml path into a key. */
public static String mergePath( List<String> path ) {
public static String mergePath( @Nullable List<String> path ) {
if( path == null || path.isEmpty() ) return "";
StringBuilder key = new StringBuilder();
for( String subKey : path ) {
@ -207,15 +210,13 @@ public final class TomlHelper {
}
/** Convenience method for creating a list of single-line comments (no \n or \r). */
public static ArrayList<String> newComment( String... lines ) {
return new ArrayList<>( Arrays.asList( lines ) );
}
public static ArrayList<String> newComment( String... lines ) { return new ArrayList<>( Arrays.asList( lines ) ); }
/** Combines an array of objects as a comma-separated string. */
public static String combineList( Object... list ) { return combineList( Arrays.asList( list ) ); }
/** Combines a list of objects as a comma-separated string. */
public static String combineList( List<Object> list ) {
public static String combineList( @Nullable List<Object> list ) {
if( list == null || list.isEmpty() ) return "";
StringBuilder key = new StringBuilder();
for( Object obj : list ) {

View file

@ -0,0 +1,7 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package fathertoast.specialmobs.common.config.file;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -0,0 +1,7 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package fathertoast.specialmobs.common.config.species;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -5,6 +5,7 @@ import fathertoast.specialmobs.common.config.file.TomlHelper;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
@ -33,7 +34,7 @@ public class AttributeList implements IStringArray {
/** @return Returns true if this object has the same value as another object. */
@Override
public boolean equals( Object other ) {
public boolean equals( @Nullable Object other ) {
if( !(other instanceof AttributeList) ) return false;
// Compare by the string list view of the object
return toStringList().equals( ((AttributeList) other).toStringList() );

View file

@ -32,7 +32,7 @@ public class BlockEntry implements Cloneable {
this( block.getBlock() );
// Add all the properties present in the block state
StateBuilder state = new StateBuilder();
StateBuilder state = new StateBuilder( BLOCK );
for( Property<? extends Comparable<?>> property : block.getProperties() ) {
state.add( property, block.getValue( property ) );
}
@ -45,13 +45,8 @@ public class BlockEntry implements Cloneable {
public BlockEntry( AbstractConfigField field, String line ) {
// Parse the base block
final String[] pair = line.split( "\\[", 2 );
final ResourceLocation regKey = new ResourceLocation( pair[0] );
if( !ForgeRegistries.BLOCKS.containsKey( regKey ) ) {
BLOCK = Blocks.AIR;
}
else {
BLOCK = ForgeRegistries.BLOCKS.getValue( regKey );
}
final Block block = ForgeRegistries.BLOCKS.getValue( new ResourceLocation( pair[0] ) );
BLOCK = block == null ? Blocks.AIR : block;
// We are done constructing if the entry is invalid or does not specify block states
if( BLOCK == Blocks.AIR || pair.length < 2 ) {
@ -143,7 +138,7 @@ public class BlockEntry implements Cloneable {
final StateContainer<Block, BlockState> stateContainer = block.getStateDefinition();
// Parse the state and build the matcher
final StateBuilder builder = new StateBuilder();
final StateBuilder builder = new StateBuilder( block );
final String[] properties = stateString.split( "," );
for( String combinedEntry : properties ) {
// Parse an individual property key-value pair
@ -271,9 +266,6 @@ public class BlockEntry implements Cloneable {
/** Can be used for building default configs to make block entries with target states. */
public StateBuilder( Block block ) { BLOCK = block; }
/** Used by block entries to build target states. Can not be built into a block entry when using this constructor. */
private StateBuilder() { BLOCK = null; }
/** @return Returns true if this state builder has no properties set. */
@SuppressWarnings( "BooleanMethodIsAlwaysInverted" )
public boolean isEmpty() { return propertiesToMatch.isEmpty(); }
@ -289,7 +281,7 @@ public class BlockEntry implements Cloneable {
/** @return Returns a block entry reflecting the current state of this builder. */
public BlockEntry toBlockEntry() {
BlockEntry target = new BlockEntry( BLOCK );
final BlockEntry target = new BlockEntry( BLOCK );
if( !isEmpty() ) {
target.MATCHERS.add( toTargetState() );
}

View file

@ -10,6 +10,7 @@ import net.minecraft.block.Blocks;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.ForgeRegistries;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -61,13 +62,11 @@ public class BlockList implements IStringArray {
/** @return A string representation of this object. */
@Override
public String toString() {
return TomlHelper.toLiteral( PRINT_LIST.toArray() );
}
public String toString() { return TomlHelper.toLiteral( PRINT_LIST.toArray() ); }
/** @return Returns true if this object has the same value as another object. */
@Override
public boolean equals( Object other ) {
public boolean equals( @Nullable Object other ) {
if( !(other instanceof BlockList) ) return false;
// Compare by the string list view of the object
return toStringList().equals( ((BlockList) other).toStringList() );
@ -109,10 +108,9 @@ public class BlockList implements IStringArray {
private void mergeFromNamespace( String namespace ) {
for( ResourceLocation regKey : ForgeRegistries.BLOCKS.getKeys() ) {
if( regKey.toString().startsWith( namespace ) ) {
BlockEntry entry = new BlockEntry( ForgeRegistries.BLOCKS.getValue( regKey ) );
if( entry.BLOCK != null && entry.BLOCK != Blocks.AIR ) {
mergeFrom( entry );
}
final Block block = ForgeRegistries.BLOCKS.getValue( regKey );
if( block != null && block != Blocks.AIR )
mergeFrom( new BlockEntry( block ) );
}
}
}

View file

@ -1,11 +1,19 @@
package fathertoast.specialmobs.common.config.util;
import com.electronwill.nightconfig.core.file.FileConfig;
import fathertoast.specialmobs.common.core.SpecialMobs;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent;
import net.minecraftforge.fml.loading.FMLPaths;
import java.io.File;
public abstract class ConfigUtil {
@Mod.EventBusSubscriber( modid = SpecialMobs.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE )
public final class ConfigUtil {
/** The current "version" of the dynamic registries. This is incremented each time dynamic registries are loaded. */
public static byte DYNAMIC_REGISTRY_VERSION;
/** The plus or minus symbol (+/-). */
public static final String PLUS_OR_MINUS = "\u00b1";
@ -44,4 +52,12 @@ public abstract class ConfigUtil {
/** @return A string representation of the file from the game directory. */
public static String toRelativePath( File gameFile ) { return FMLPaths.GAMEDIR.get().relativize( gameFile.toPath() ).toString(); }
/**
* Called when a server (integrated or dedicated) is about to start.
*
* @param event The event data.
*/
@SubscribeEvent( priority = EventPriority.NORMAL )
public static void onServerAboutToStart( FMLServerAboutToStartEvent event ) { DYNAMIC_REGISTRY_VERSION++; }
}

View file

@ -5,6 +5,7 @@ import fathertoast.specialmobs.common.config.file.TomlHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
@ -45,7 +46,7 @@ public class EntityList implements IStringArray {
/** @return Returns true if this object has the same value as another object. */
@Override
public boolean equals( Object other ) {
public boolean equals( @Nullable Object other ) {
if( !(other instanceof EntityList) ) return false;
// Compare by the string list view of the object
return toStringList().equals( ((EntityList) other).toStringList() );
@ -63,7 +64,8 @@ public class EntityList implements IStringArray {
}
/** @return True if the entity is contained in this list. */
public boolean contains( Entity entity ) {
public boolean contains( @Nullable Entity entity ) {
if( entity == null ) return false;
final EntityEntry targetEntry = new EntityEntry( entity );
for( EntityEntry currentEntry : ENTRIES ) {
currentEntry.checkClass( entity.level );
@ -77,7 +79,9 @@ public class EntityList implements IStringArray {
* @param entity The entity to retrieve values for.
* @return The array of values of the best-match entry. Returns null if the entity is not contained in this entity list.
*/
public double[] getValues( Entity entity ) {
@Nullable
public double[] getValues( @Nullable Entity entity ) {
if( entity == null ) return null;
final EntityEntry targetEntry = new EntityEntry( entity );
EntityEntry bestMatch = null;
for( EntityEntry currentEntry : ENTRIES ) {
@ -101,7 +105,7 @@ public class EntityList implements IStringArray {
* @see #setSingleValue()
* @see #setSinglePercent()
*/
public double getValue( Entity entity ) {
public double getValue( @Nullable Entity entity ) {
final double[] values = getValues( entity );
return values == null || values.length < 1 ? 0.0 : values[0];
}
@ -112,7 +116,7 @@ public class EntityList implements IStringArray {
* is not contained in this entity list or has no values specified. This should only be used for 'single percent' lists.
* @see #setSinglePercent()
*/
public boolean rollChance( LivingEntity entity ) {
public boolean rollChance( @Nullable LivingEntity entity ) {
return ENTRIES.length > 0 && entity != null && entity.getRandom().nextDouble() < getValue( entity );
}

View file

@ -0,0 +1,112 @@
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.dimension.DimensionTypeEnvironment;
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 net.minecraft.util.RegistryKey;
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;
import java.util.ArrayList;
import java.util.List;
/**
* One condition-value entry in an environment list. Uses a 'lazy' implementation so any needed registries are
* not polled until this entry is actually used.
*/
@SuppressWarnings( "unused" )
public class EnvironmentEntry {
/** The value given to this entry. */
public final double VALUE;
/** The conditions that define this entry's environment. */
private final AbstractEnvironment[] CONDITIONS;
/** Creates an entry with the specified values. */
public EnvironmentEntry( double value, List<AbstractEnvironment> conditions ) { this( value, conditions.toArray( new AbstractEnvironment[0] ) ); }
/** Creates an entry with the specified values. */
public EnvironmentEntry( double value, AbstractEnvironment... conditions ) {
VALUE = value;
CONDITIONS = conditions;
}
/** @return Returns true if all this entry's conditions match the provided environment. */
public boolean matches( World world, @Nullable BlockPos pos ) {
for( AbstractEnvironment condition : CONDITIONS ) {
if( !condition.matches( world, pos ) ) return false;
}
return true;
}
/**
* @return The string representation of this environment entry, as it would appear in a config file.
* <p>
* Format is "value condition1 state1 & condition2 state2 & ...".
*/
@Override
public String toString() {
// Start with the value
final StringBuilder str = new StringBuilder().append( VALUE ).append( ' ' );
// List all conditions
boolean first = true;
for( AbstractEnvironment condition : CONDITIONS ) {
if( first ) first = false;
else str.append( " & " );
str.append( condition );
}
return str.toString();
}
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.
*/
public static class Builder {
private final double VALUE;
private final ArrayList<AbstractEnvironment> CONDITIONS = new ArrayList<>();
private Builder( double value ) { VALUE = value; }
public EnvironmentEntry build() { return new EnvironmentEntry( VALUE, CONDITIONS ); }
public Builder in( AbstractEnvironment condition ) {
CONDITIONS.add( condition );
return this;
}
// Dimension-based
public Builder inDimensionType( RegistryKey<DimensionType> dimType ) { return in( new DimensionTypeEnvironment( dimType ) ); }
// Biome-based
public Builder inBiome( RegistryKey<Biome> biome ) { return in( new BiomeEnvironment( biome ) ); }
// Position-based
public Builder inStructure( Structure<?> structure ) { return in( new StructureEnvironment( structure ) ); }
public Builder belowY( int y ) { return in( new YEnvironment( ComparisonOperator.LESS_THAN, y ) ); }
public Builder atLeastY( int y ) { return in( new YEnvironment( ComparisonOperator.GREATER_OR_EQUAL, y ) ); }
public Builder belowSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_THAN, 0 ) ); }
public Builder atLeastSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.GREATER_OR_EQUAL, 0 ) ); }
// Time-based
}
}

View file

@ -0,0 +1,100 @@
package fathertoast.specialmobs.common.config.util;
import fathertoast.specialmobs.common.config.field.DoubleField;
import fathertoast.specialmobs.common.config.field.IStringArray;
import fathertoast.specialmobs.common.config.file.TomlHelper;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* A list of condition-value entries used to link one number to specific environments.
*/
@SuppressWarnings( { "unused", "SameParameterValue" } )
public class EnvironmentList implements IStringArray {
/** The condition-value entries in this list. */
private final EnvironmentEntry[] ENTRIES;
/** The minimum value accepted for entry values in this list. */
private double minValue = Double.NEGATIVE_INFINITY;
/** The maximum value accepted for entry values in this list. */
private double maxValue = Double.POSITIVE_INFINITY;
/**
* Create a new environment list from a list of entries.
* <p>
* By default, environment list value(s) can be any numerical double.
* This can be changed with helper methods that alter values' bounds and return 'this'.
*/
public EnvironmentList( List<EnvironmentEntry> entries ) { this( entries.toArray( new EnvironmentEntry[0] ) ); }
/**
* Create a new environment list from an array of entries. Used for creating default configs.
* <p>
* By default, environment list value(s) can be any numerical double.
* This can be changed with helper methods that alter values' bounds and return 'this'.
*/
public EnvironmentList( EnvironmentEntry... entries ) { ENTRIES = entries; }
/** @return A string representation of this object. */
@Override
public String toString() { return TomlHelper.toLiteral( toStringList().toArray() ); }
/** @return Returns true if this object has the same value as another object. */
@Override
public boolean equals( @Nullable Object other ) {
if( !(other instanceof EnvironmentList) ) return false;
// Compare by the string list view of the object
return toStringList().equals( ((EnvironmentList) other).toStringList() );
}
/** @return A list of strings that will represent this object when written to a toml file. */
@Override
public List<String> toStringList() {
// Create a list of the entries in string format
final List<String> list = new ArrayList<>( ENTRIES.length );
for( EnvironmentEntry entry : ENTRIES ) {
list.add( entry.toString() );
}
return list;
}
/** @return The value matching the given environment, or the default value if no matching environment is defined. */
public double getOrElse( World world, @Nullable BlockPos pos, DoubleField defaultValue ) {
return getOrElse( world, pos, defaultValue.get() );
}
/** @return The value matching the given environment, or the default value if no matching environment is defined. */
public double getOrElse( World world, @Nullable BlockPos pos, double defaultValue ) {
final Double value = get( world, pos );
return value == null ? defaultValue : value;
}
/** @return The value matching the given environment, or null if no matching environment is defined. */
@Nullable
public Double get( World world, @Nullable BlockPos pos ) {
for( EnvironmentEntry entry : ENTRIES ) {
if( entry.matches( world, pos ) ) return entry.VALUE;
}
return null;
}
/** Bounds entry values in this list to the specified range. */
public EnvironmentList setRange( DoubleField.Range range ) { return setRange( range.MIN, range.MAX ); }
/** Bounds entry values in this list to the specified limits, inclusive. */
public EnvironmentList setRange( double min, double max ) {
minValue = min;
maxValue = max;
return this;
}
/** @return The minimum value that can be given to entry values. */
public double getMinValue() { return minValue; }
/** @return The maximum value that can be given to entry values. */
public double getMaxValue() { return maxValue; }
}

View file

@ -8,6 +8,7 @@ import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.IForgeRegistryEntry;
import javax.annotation.Nullable;
import java.util.*;
/**
@ -25,9 +26,7 @@ public class RegistryEntryList<T extends IForgeRegistryEntry<T>> implements IStr
/** The list used to write back to file. */
protected final List<String> PRINT_LIST = new ArrayList<>();
protected RegistryEntryList( IForgeRegistry<T> registry ) {
REGISTRY = registry;
}
protected RegistryEntryList( IForgeRegistry<T> registry ) { REGISTRY = registry; }
/**
* Create a new registry entry list from an array of entries. Used for creating default configs.
@ -79,13 +78,11 @@ public class RegistryEntryList<T extends IForgeRegistryEntry<T>> implements IStr
/** @return A string representation of this object. */
@Override
public String toString() {
return TomlHelper.toLiteral( PRINT_LIST.toArray() );
}
public String toString() { return TomlHelper.toLiteral( PRINT_LIST.toArray() ); }
/** @return Returns true if this object has the same value as another object. */
@Override
public boolean equals( Object other ) {
public boolean equals( @Nullable Object other ) {
if( !(other instanceof RegistryEntryList) ) return false;
// Compare by the registries used and string list view of the object
return getRegistry() == ((RegistryEntryList<?>) other).getRegistry() &&
@ -100,7 +97,7 @@ public class RegistryEntryList<T extends IForgeRegistryEntry<T>> implements IStr
public boolean isEmpty() { return UNDERLYING_SET.isEmpty(); }
/** @return Returns true if the entry is contained in this list. */
public boolean contains( T entry ) { return UNDERLYING_SET.contains( entry ); }
public boolean contains( @Nullable T entry ) { return UNDERLYING_SET.contains( entry ); }
/** @return Adds the registry entry if it exists and isn't already present, returns true if successful. */
protected boolean mergeFrom( ResourceLocation regKey ) {

View file

@ -13,6 +13,7 @@ import java.util.*;
* <p>
* Creates a config field for each item so weights can be defined by the user.
*/
@SuppressWarnings( "unused" )
public class WeightedList<T extends WeightedList.Value> {
/** The spec used by this config that defines the file's format. */
protected final ToastConfigSpec SPEC;
@ -110,6 +111,7 @@ public class WeightedList<T extends WeightedList.Value> {
default int getDefaultWeight() { return 1; }
/** @return Returns the comment for this object. */
@Nullable
default String[] getComment() { return null; }
}
}

View file

@ -0,0 +1,21 @@
package fathertoast.specialmobs.common.config.util.environment;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nullable;
public abstract class AbstractEnvironment {
/** @return The string representation of this environment, as it would appear in a config file. */
@Override
public final String toString() { return name() + " " + value(); }
/** @return The string name of this environment, as it would appear in a config file. */
public abstract String name();
/** @return The string value of this environment, as it would appear in a config file. */
public abstract String value();
/** @return Returns true if this environment matches the provided environment. */
public abstract boolean matches( World world, @Nullable BlockPos pos );
}

View file

@ -0,0 +1,87 @@
package fathertoast.specialmobs.common.config.util.environment;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
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;
public abstract class CompareFloatEnvironment extends AbstractEnvironment {
/** How the actual value is compared to this environment's value. */
public final ComparisonOperator COMPARATOR;
/** The value for this environment. */
public final float VALUE;
public CompareFloatEnvironment( ComparisonOperator op, float value ) {
COMPARATOR = op;
VALUE = value;
}
public CompareFloatEnvironment( AbstractConfigField field, String line ) {
if( line.isEmpty() ) {
COMPARATOR = ComparisonOperator.LESS_THAN;
VALUE = 0.0F;
SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Not defined. Defaulting to \"{}\". Invalid entry: {}",
field.getClass(), field.getKey(), value(), line );
}
else {
final ComparisonOperator op = ComparisonOperator.parse( line );
if( op == null ) {
COMPARATOR = ComparisonOperator.LESS_THAN;
SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Comparison not defined (must be in the set [ {} ]). Defaulting to \"{}\". Invalid entry: {}",
field.getClass(), field.getKey(), TomlHelper.literalList( (Object[]) ComparisonOperator.values() ), COMPARATOR, line );
}
else COMPARATOR = op;
VALUE = parseValue( field, line, line.substring( COMPARATOR.toString().length() ).trim() );
}
}
/** @return Parses the value and returns a valid result. */
private float parseValue( AbstractConfigField field, String line, String arg ) {
// Try to parse the value
float value;
try {
value = Float.parseFloat( arg );
}
catch( NumberFormatException ex ) {
SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Value not defined (must be a float). Defaulting to '0'. Invalid entry: {}",
field.getClass(), field.getKey(), line );
value = 0.0F;
}
// Verify value is within range
if( value < getMinValue() ) {
SpecialMobs.LOG.warn( "Value for {} \"{}\" is below the minimum ({})! Clamping value. Invalid value: {}",
field.getClass(), field.getKey(), getMinValue(), value );
value = getMinValue();
}
else if( value > getMaxValue() ) {
SpecialMobs.LOG.warn( "Value for {} \"{}\" is above the maximum ({})! Clamping value. Invalid value: {}",
field.getClass(), field.getKey(), getMaxValue(), value );
value = getMaxValue();
}
return value;
}
/** @return The minimum value that can be given to the value. */
protected float getMinValue() { return Float.NEGATIVE_INFINITY; }
/** @return The maximum value that can be given to the value. */
protected float getMaxValue() { return Float.POSITIVE_INFINITY; }
/** @return The string value of this environment, as it would appear in a config file. */
@Override
public String value() { return COMPARATOR + " " + VALUE; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public boolean matches( World world, @Nullable BlockPos pos ) {
final float actual = getActual( world, pos );
return !Float.isNaN( actual ) && COMPARATOR.apply( actual, VALUE );
}
/** @return Returns the actual value to compare, or Float.NaN if there isn't enough information. */
public abstract float getActual( World world, @Nullable BlockPos pos );
}

View file

@ -0,0 +1,87 @@
package fathertoast.specialmobs.common.config.util.environment;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
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;
public abstract class CompareIntEnvironment extends AbstractEnvironment {
/** How the actual value is compared to this environment's value. */
public final ComparisonOperator COMPARATOR;
/** The value for this environment. */
public final int VALUE;
public CompareIntEnvironment( ComparisonOperator op, int value ) {
COMPARATOR = op;
VALUE = value;
}
public CompareIntEnvironment( AbstractConfigField field, String line ) {
if( line.isEmpty() ) {
COMPARATOR = ComparisonOperator.LESS_THAN;
VALUE = 0;
SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Not defined. Defaulting to \"{}\". Invalid entry: {}",
field.getClass(), field.getKey(), value(), line );
}
else {
final ComparisonOperator op = ComparisonOperator.parse( line );
if( op == null ) {
COMPARATOR = ComparisonOperator.LESS_THAN;
SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Comparison not defined (must be in the set [ {} ]). Defaulting to \"{}\". Invalid entry: {}",
field.getClass(), field.getKey(), TomlHelper.literalList( (Object[]) ComparisonOperator.values() ), COMPARATOR, line );
}
else COMPARATOR = op;
VALUE = parseValue( field, line, line.substring( COMPARATOR.toString().length() ).trim() );
}
}
/** @return Parses the value and returns a valid result. */
private int parseValue( AbstractConfigField field, String line, String arg ) {
// Try to parse the value
int value;
try {
value = Integer.parseInt( arg );
}
catch( NumberFormatException ex ) {
SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Value not defined (must be an integer). Defaulting to '0'. Invalid entry: {}",
field.getClass(), field.getKey(), line );
value = 0;
}
// Verify value is within range
if( value < getMinValue() ) {
SpecialMobs.LOG.warn( "Value for {} \"{}\" is below the minimum ({})! Clamping value. Invalid value: {}",
field.getClass(), field.getKey(), getMinValue(), value );
value = getMinValue();
}
else if( value > getMaxValue() ) {
SpecialMobs.LOG.warn( "Value for {} \"{}\" is above the maximum ({})! Clamping value. Invalid value: {}",
field.getClass(), field.getKey(), getMaxValue(), value );
value = getMaxValue();
}
return value;
}
/** @return The minimum value that can be given to the value. */
protected int getMinValue() { return Integer.MIN_VALUE; }
/** @return The maximum value that can be given to the value. */
protected int getMaxValue() { return Integer.MAX_VALUE; }
/** @return The string value of this environment, as it would appear in a config file. */
@Override
public String value() { return COMPARATOR + " " + VALUE; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public boolean matches( World world, @Nullable BlockPos pos ) {
final Integer actual = getActual( world, pos );
return actual != null && COMPARATOR.apply( actual, VALUE );
}
/** @return Returns the actual value to compare, or null if there isn't enough information. */
@Nullable
public abstract Integer getActual( World world, @Nullable BlockPos pos );
}

View file

@ -0,0 +1,87 @@
package fathertoast.specialmobs.common.config.util.environment;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
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;
public abstract class CompareLongEnvironment extends AbstractEnvironment {
/** How the actual value is compared to this environment's value. */
public final ComparisonOperator COMPARATOR;
/** The value for this environment. */
public final long VALUE;
public CompareLongEnvironment( ComparisonOperator op, long value ) {
COMPARATOR = op;
VALUE = value;
}
public CompareLongEnvironment( AbstractConfigField field, String line ) {
if( line.isEmpty() ) {
COMPARATOR = ComparisonOperator.LESS_THAN;
VALUE = 0L;
SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Not defined. Defaulting to \"{}\". Invalid entry: {}",
field.getClass(), field.getKey(), value(), line );
}
else {
final ComparisonOperator op = ComparisonOperator.parse( line );
if( op == null ) {
COMPARATOR = ComparisonOperator.LESS_THAN;
SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Comparison not defined (must be in the set [ {} ]). Defaulting to \"{}\". Invalid entry: {}",
field.getClass(), field.getKey(), TomlHelper.literalList( (Object[]) ComparisonOperator.values() ), COMPARATOR, line );
}
else COMPARATOR = op;
VALUE = parseValue( field, line, line.substring( COMPARATOR.toString().length() ).trim() );
}
}
/** @return Parses the value and returns a valid result. */
private long parseValue( AbstractConfigField field, String line, String arg ) {
// Try to parse the value
long value;
try {
value = Long.parseLong( arg );
}
catch( NumberFormatException ex ) {
SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Value not defined (must be a long). Defaulting to '0'. Invalid entry: {}",
field.getClass(), field.getKey(), line );
value = 0;
}
// Verify value is within range
if( value < getMinValue() ) {
SpecialMobs.LOG.warn( "Value for {} \"{}\" is below the minimum ({})! Clamping value. Invalid value: {}",
field.getClass(), field.getKey(), getMinValue(), value );
value = getMinValue();
}
else if( value > getMaxValue() ) {
SpecialMobs.LOG.warn( "Value for {} \"{}\" is above the maximum ({})! Clamping value. Invalid value: {}",
field.getClass(), field.getKey(), getMaxValue(), value );
value = getMaxValue();
}
return value;
}
/** @return The minimum value that can be given to the value. */
protected long getMinValue() { return Long.MIN_VALUE; }
/** @return The maximum value that can be given to the value. */
protected long getMaxValue() { return Long.MAX_VALUE; }
/** @return The string value of this environment, as it would appear in a config file. */
@Override
public String value() { return COMPARATOR + " " + VALUE; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public boolean matches( World world, @Nullable BlockPos pos ) {
final Long actual = getActual( world, pos );
return actual != null && COMPARATOR.apply( actual, VALUE );
}
/** @return Returns the actual value to compare, or null if there isn't enough information. */
@Nullable
public abstract Long getActual( World world, @Nullable BlockPos pos );
}

View file

@ -0,0 +1,78 @@
package fathertoast.specialmobs.common.config.util.environment;
import javax.annotation.Nullable;
public enum ComparisonOperator {
NOT_EQUAL_TO( "!=" ), LESS_OR_EQUAL( "<=" ), GREATER_OR_EQUAL( ">=" ),
EQUAL_TO( "=" ), LESS_THAN( "<" ), GREATER_THAN( ">" );
private final String LITERAL;
ComparisonOperator( String str ) { LITERAL = str; }
@Override
public String toString() { return LITERAL; }
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;
}
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;
}
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;
}
throw new IllegalStateException( "Long comparison implementation is invalid! :(" );
}
/** @return The operator described by a given string, or null if invalid. */
@Nullable
public static ComparisonOperator parse( String op ) {
for( ComparisonOperator operator : values() ) {
if( op.startsWith( operator.LITERAL ) ) return operator;
}
return null;
}
}

View file

@ -0,0 +1,78 @@
package fathertoast.specialmobs.common.config.util.environment;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
import fathertoast.specialmobs.common.core.SpecialMobs;
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.World;
import net.minecraft.world.server.ServerWorld;
import javax.annotation.Nullable;
/**
* Dynamic registries are contained in {@link net.minecraft.util.registry.DynamicRegistries}
*/
public abstract class DynamicRegistryEnvironment<T> extends AbstractEnvironment {
/** The field containing this entry. We save a reference to help improve error/warning reports. */
private final AbstractConfigField FIELD;
/** If true, the condition is inverted. */
protected final boolean INVERT;
/** The registry key for this environment. */
private final ResourceLocation REGISTRY_KEY;
private T registryEntry;
/** 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;
REGISTRY_KEY = regKey;
}
public DynamicRegistryEnvironment( AbstractConfigField field, String line ) {
FIELD = field;
INVERT = line.startsWith( "!" );
REGISTRY_KEY = new ResourceLocation( INVERT ? line.substring( 1 ) : line );
}
/** @return The string value of this environment, as it would appear in a config file. */
@Override
public final String value() { return (INVERT ? "!" : "") + REGISTRY_KEY.toString(); }
/** @return The registry used. */
public abstract RegistryKey<Registry<T>> getRegistry();
/** @return Returns true if this environment matches the provided environment. */
@Override
public final boolean matches( World world, @Nullable BlockPos pos ) {
if( world instanceof ServerWorld )
return matches( (ServerWorld) world, pos ); // These don't work on the client :(
return INVERT;
}
/** @return Returns true if this environment matches the provided environment. */
public abstract boolean matches( ServerWorld world, @Nullable BlockPos pos );
/** @return The target registry object. */
@Nullable
public final T getRegistryEntry( ServerWorld world ) {
if( version != ConfigUtil.DYNAMIC_REGISTRY_VERSION ) {
version = ConfigUtil.DYNAMIC_REGISTRY_VERSION;
final Registry<T> registry = world.getServer().registryAccess().registryOrThrow( getRegistry() );
registryEntry = registry.get( REGISTRY_KEY );
if( registryEntry == null ) {
SpecialMobs.LOG.info( "Missing entry for {} \"{}\"! Not present in registry \"{}\". Missing entry: {}",
FIELD.getClass(), FIELD.getKey(), getRegistry().getRegistryName(), REGISTRY_KEY );
}
}
return registryEntry;
}
}

View file

@ -0,0 +1,87 @@
package fathertoast.specialmobs.common.config.util.environment;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
import fathertoast.specialmobs.common.core.SpecialMobs;
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.World;
import net.minecraft.world.server.ServerWorld;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Dynamic registries are contained in {@link net.minecraft.util.registry.DynamicRegistries}
*/
public abstract class DynamicRegistryGroupEnvironment<T> extends AbstractEnvironment {
/** The field containing this entry. We save a reference to help improve error/warning reports. */
private final AbstractConfigField FIELD;
/** If true, the condition is inverted. */
protected final boolean INVERT;
/** The namespace for this environment. */
private final String NAMESPACE;
private List<T> registryEntries;
/** 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;
NAMESPACE = regKey.toString();
}
public DynamicRegistryGroupEnvironment( AbstractConfigField field, String line ) {
FIELD = field;
INVERT = line.startsWith( "!" );
NAMESPACE = line.substring( INVERT ? 1 : 0, line.length() - 1 );
}
/** @return The string value of this environment, as it would appear in a config file. */
@Override
public final String value() { return (INVERT ? "!" : "") + NAMESPACE + "*"; }
/** @return The registry used. */
public abstract RegistryKey<Registry<T>> getRegistry();
/** @return Returns true if this environment matches the provided environment. */
@Override
public final boolean matches( World world, @Nullable BlockPos pos ) {
if( world instanceof ServerWorld )
return matches( (ServerWorld) world, pos ); // These don't work on the client :(
return INVERT;
}
/** @return Returns true if this environment matches the provided environment. */
public abstract boolean matches( ServerWorld world, @Nullable BlockPos pos );
/** @return The target registry object. */
protected final List<T> getRegistryEntries( ServerWorld world ) {
if( version != ConfigUtil.DYNAMIC_REGISTRY_VERSION ) {
version = ConfigUtil.DYNAMIC_REGISTRY_VERSION;
registryEntries = new ArrayList<>();
final Registry<T> registry = world.getServer().registryAccess().registryOrThrow( getRegistry() );
for( ResourceLocation regKey : registry.keySet() ) {
if( regKey.toString().startsWith( NAMESPACE ) ) {
final T entry = registry.get( regKey );
if( entry != null ) registryEntries.add( entry );
}
}
if( registryEntries.isEmpty() ) {
SpecialMobs.LOG.info( "Namespace entry for {} \"{}\" did not match anything in registry \"{}\"! Questionable entry: {}",
FIELD == null ? "DEFAULT" : FIELD.getClass(), FIELD == null ? "DEFAULT" : FIELD.getKey(), getRegistry().getRegistryName(), NAMESPACE );
}
registryEntries = Collections.unmodifiableList( registryEntries );
}
return registryEntries;
}
}

View file

@ -0,0 +1,41 @@
package fathertoast.specialmobs.common.config.util.environment;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.config.file.TomlHelper;
import fathertoast.specialmobs.common.core.SpecialMobs;
import java.util.Locale;
public abstract class EnumEnvironment<T extends Enum<T>> extends AbstractEnvironment {
/** If true, the condition is inverted. */
protected final boolean INVERT;
/** 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;
}
public EnumEnvironment( AbstractConfigField field, String line, T[] validValues ) {
INVERT = line.startsWith( "!" );
VALUE = parseValue( field, line, validValues, INVERT ? line.substring( 1 ) : line );
}
/** @return Attempts to parse the string literal as one of the valid values and returns it, or null if invalid. */
private T parseValue( AbstractConfigField field, String line, T[] validValues, String name ) {
for( T value : validValues ) {
if( value.name().equalsIgnoreCase( name ) ) return value;
}
// Value cannot be parsed
SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Value not defined (must be in the set [ {} ]). Defaulting to {}. Invalid entry: {}",
field.getClass(), field.getKey(), TomlHelper.literalList( (Object[]) validValues ), TomlHelper.toLiteral(), line );
return validValues[0];
}
/** @return The string value of this environment, as it would appear in a config file. */
@Override
public final String value() { return (INVERT ? "!" : "") + VALUE.name().toLowerCase( Locale.ROOT ); }
}

View file

@ -0,0 +1,59 @@
package fathertoast.specialmobs.common.config.util.environment;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.core.SpecialMobs;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.IForgeRegistryEntry;
import javax.annotation.Nullable;
/**
* Registries are contained in {@link net.minecraftforge.registries.ForgeRegistries}
*/
public abstract class RegistryEnvironment<T extends IForgeRegistryEntry<T>> extends AbstractEnvironment {
/** The field containing this entry. We save a reference to help improve error/warning reports. */
private final AbstractConfigField FIELD;
/** If true, the condition is inverted. */
protected final boolean INVERT;
/** The registry key for this environment. */
private final ResourceLocation REGISTRY_KEY;
private T registryEntry;
public RegistryEnvironment( T regEntry ) { this( regEntry, false ); }
public RegistryEnvironment( T regEntry, boolean invert ) {
FIELD = null;
INVERT = invert;
REGISTRY_KEY = regEntry.getRegistryName();
registryEntry = regEntry;
}
public RegistryEnvironment( AbstractConfigField field, String line ) {
FIELD = field;
INVERT = line.startsWith( "!" );
REGISTRY_KEY = new ResourceLocation( INVERT ? line.substring( 1 ) : line );
}
/** @return The string value of this environment, as it would appear in a config file. */
@Override
public final String value() { return (INVERT ? "!" : "") + REGISTRY_KEY.toString(); }
/** @return The registry used. */
public abstract IForgeRegistry<T> getRegistry();
/** @return The registry entry. */
@Nullable
protected final T getRegistryEntry() {
if( registryEntry == null ) {
if( !getRegistry().containsKey( REGISTRY_KEY ) ) {
SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Not present in registry \"{}\". Invalid entry: {}",
FIELD.getClass(), FIELD.getKey(), getRegistry().getRegistryName(), REGISTRY_KEY );
}
registryEntry = getRegistry().getValue( REGISTRY_KEY );
}
return registryEntry;
}
}

View file

@ -0,0 +1,67 @@
package fathertoast.specialmobs.common.config.util.environment;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.core.SpecialMobs;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.IForgeRegistryEntry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Registries are contained in {@link net.minecraftforge.registries.ForgeRegistries}
*/
public abstract class RegistryGroupEnvironment<T extends IForgeRegistryEntry<T>> extends AbstractEnvironment {
/** The field containing this entry. We save a reference to help improve error/warning reports. */
private final AbstractConfigField FIELD;
/** If true, the condition is inverted. */
protected final boolean INVERT;
/** The namespace for this environment. */
private final String NAMESPACE;
private List<T> registryEntries;
public RegistryGroupEnvironment( T regEntry ) { this( regEntry, false ); }
public RegistryGroupEnvironment( T regEntry, boolean invert ) {
FIELD = null;
INVERT = invert;
//noinspection ConstantConditions
NAMESPACE = regEntry.getRegistryName().toString();
}
public RegistryGroupEnvironment( AbstractConfigField field, String line ) {
FIELD = field;
INVERT = line.startsWith( "!" );
NAMESPACE = line.substring( INVERT ? 1 : 0, line.length() - 1 );
}
/** @return The string value of this environment, as it would appear in a config file. */
@Override
public final String value() { return (INVERT ? "!" : "") + NAMESPACE + "*"; }
/** @return The registry used. */
public abstract IForgeRegistry<T> getRegistry();
/** @return The registry entries. */
protected final List<T> getRegistryEntries() {
if( registryEntries == null ) {
registryEntries = new ArrayList<>();
for( ResourceLocation regKey : getRegistry().getKeys() ) {
if( regKey.toString().startsWith( NAMESPACE ) ) {
final T entry = getRegistry().getValue( regKey );
if( entry != null ) registryEntries.add( entry );
}
}
if( registryEntries.isEmpty() ) {
SpecialMobs.LOG.warn( "Namespace entry for {} \"{}\" did not match anything in registry \"{}\"! Questionable entry: {}",
FIELD == null ? "DEFAULT" : FIELD.getClass(), FIELD == null ? "DEFAULT" : FIELD.getKey(), getRegistry().getRegistryName(), NAMESPACE );
}
registryEntries = Collections.unmodifiableList( registryEntries );
}
return registryEntries;
}
}

View file

@ -0,0 +1,52 @@
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.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<BiomeCategoryEnvironment.Value> {
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 BiomeCategoryEnvironment( Value value ) { super( value ); }
public BiomeCategoryEnvironment( Value value, boolean invert ) { super( value, invert ); }
public BiomeCategoryEnvironment( AbstractConfigField field, String line ) { super( field, line, Value.values() ); }
/** @return The string name of this environment, as it would appear in a config file. */
@Override
public String name() { return EnvironmentListField.ENV_BIOME_CATEGORY; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public boolean matches( World world, @Nullable BlockPos pos ) {
return (pos != null && VALUE.BASE.equals( world.getBiome( pos ).getBiomeCategory() )) != INVERT;
}
}

View file

@ -0,0 +1,36 @@
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.DynamicRegistryEnvironment;
import net.minecraft.util.RegistryKey;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.server.ServerWorld;
import javax.annotation.Nullable;
public class BiomeEnvironment extends DynamicRegistryEnvironment<Biome> {
public BiomeEnvironment( RegistryKey<Biome> biome ) { super( biome.getRegistryName() ); }
public BiomeEnvironment( RegistryKey<Biome> biome, boolean invert ) { super( biome.getRegistryName(), invert ); }
public BiomeEnvironment( 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; }
/** @return The registry used. */
@Override
public RegistryKey<Registry<Biome>> getRegistry() { return Registry.BIOME_REGISTRY; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public boolean matches( ServerWorld world, @Nullable BlockPos pos ) {
final Biome entry = getRegistryEntry( world );
return (entry != null && pos != null && entry.equals( world.getBiome( pos ) )) != INVERT;
}
}

View file

@ -0,0 +1,43 @@
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.DynamicRegistryGroupEnvironment;
import net.minecraft.util.RegistryKey;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.server.ServerWorld;
import javax.annotation.Nullable;
import java.util.List;
public class BiomeGroupEnvironment extends DynamicRegistryGroupEnvironment<Biome> {
public BiomeGroupEnvironment( RegistryKey<Biome> biome ) { super( biome.getRegistryName() ); }
public BiomeGroupEnvironment( RegistryKey<Biome> biome, boolean invert ) { super( biome.getRegistryName(), invert ); }
public BiomeGroupEnvironment( 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; }
/** @return The registry used. */
@Override
public RegistryKey<Registry<Biome>> getRegistry() { return Registry.BIOME_REGISTRY; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public final boolean matches( ServerWorld world, @Nullable BlockPos pos ) {
final Biome target = pos == null ? null : world.getBiome( pos );
if( target != null ) {
final List<Biome> entries = getRegistryEntries( world );
for( Biome entry : entries ) {
if( entry.equals( target ) ) return !INVERT;
}
}
return INVERT;
}
}

View file

@ -0,0 +1,37 @@
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 BiomeTemperatureEnvironment( ComparisonOperator op, float value ) { super( op, value ); }
public BiomeTemperatureEnvironment( AbstractConfigField field, String line ) { super( field, TemperatureEnvironment.handleTempInput( 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 ) {
return pos == null ? Float.NaN : world.getBiome( pos ).getBaseTemperature();
}
}

View file

@ -0,0 +1,37 @@
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 net.minecraft.world.biome.Biome;
import javax.annotation.Nullable;
public class RainfallEnvironment extends CompareFloatEnvironment {
public RainfallEnvironment( ComparisonOperator op, float value ) { super( op, value ); }
public RainfallEnvironment( 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_RAINFALL; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public boolean matches( World world, @Nullable BlockPos pos ) {
// Handle the special case of no rainfall
if( COMPARATOR == ComparisonOperator.EQUAL_TO && VALUE == 0.0F )
return pos != null && world.getBiome( pos ).getPrecipitation() == Biome.RainType.NONE;
return super.matches( world, pos );
}
/** @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 ) {
return pos == null ? Float.NaN : world.getBiome( pos ).getDownfall();
}
}

View file

@ -0,0 +1,47 @@
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 TemperatureEnvironment extends CompareFloatEnvironment {
public static final String FREEZING = "freezing";
public static String handleTempInput( String line ) {
if( line.equalsIgnoreCase( FREEZING ) )
return ComparisonOperator.LESS_THAN + " " + 0.15F;
if( line.equalsIgnoreCase( "!" + FREEZING ) )
return ComparisonOperator.GREATER_THAN + " " + Math.nextDown( 0.15F );
return line;
}
public TemperatureEnvironment( ComparisonOperator op, float value ) { super( op, value ); }
public TemperatureEnvironment( AbstractConfigField field, String line ) { super( field, handleTempInput( line ) ); }
/** @return The string name of this environment, as it would appear in a config file. */
@Override
public String name() { return EnvironmentListField.ENV_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 FREEZING;
if( COMPARATOR == ComparisonOperator.GREATER_THAN && VALUE == Math.nextDown( 0.15F ) )
return "!" + 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 ) {
return pos == null ? Float.NaN : world.getBiome( pos ).getTemperature( pos );
}
}

View file

@ -0,0 +1,7 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package fathertoast.specialmobs.common.config.util.environment.biome;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -0,0 +1,51 @@
package fathertoast.specialmobs.common.config.util.environment.dimension;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
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.DimensionType;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.function.Function;
public class DimensionPropertyEnvironment extends EnumEnvironment<DimensionPropertyEnvironment.Value> {
/**
* Represents all boolean values defined by dimension type, named to match data pack format.
*
* @see <a href="https://minecraft.fandom.com/wiki/Custom_dimension#Syntax">Data pack format (Minecraft Wiki)</a>
*/
public enum Value {
@SuppressWarnings( "SpellCheckingInspection" )
ULTRAWARM( DimensionType::ultraWarm ),
NATURAL( DimensionType::natural ),
HAS_SKYLIGHT( DimensionType::hasSkyLight ),
HAS_CEILING( DimensionType::hasCeiling ),
FIXED_TIME( DimensionType::hasFixedTime ),
PIGLIN_SAFE( DimensionType::piglinSafe ),
BED_WORKS( DimensionType::bedWorks ),
RESPAWN_ANCHOR_WORKS( DimensionType::respawnAnchorWorks ),
HAS_RAIDS( DimensionType::hasRaids );
private final Function<DimensionType, Boolean> SUPPLIER;
Value( Function<DimensionType, Boolean> supplier ) { SUPPLIER = supplier; }
public boolean of( DimensionType dimType ) { return SUPPLIER.apply( dimType ); }
}
public DimensionPropertyEnvironment( Value value ) { super( value ); }
public DimensionPropertyEnvironment( Value value, boolean invert ) { super( value, invert ); }
public DimensionPropertyEnvironment( AbstractConfigField field, String line ) { super( field, line, Value.values() ); }
/** @return The string name of this environment, as it would appear in a config file. */
@Override
public String name() { return EnvironmentListField.ENV_DIMENSION_PROPERTY; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public boolean matches( World world, @Nullable BlockPos pos ) { return VALUE.of( world.dimensionType() ) != INVERT; }
}

View file

@ -0,0 +1,36 @@
package fathertoast.specialmobs.common.config.util.environment.dimension;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.config.field.EnvironmentListField;
import fathertoast.specialmobs.common.config.util.environment.DynamicRegistryEnvironment;
import net.minecraft.util.RegistryKey;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.DimensionType;
import net.minecraft.world.server.ServerWorld;
import javax.annotation.Nullable;
public class DimensionTypeEnvironment extends DynamicRegistryEnvironment<DimensionType> {
public DimensionTypeEnvironment( RegistryKey<DimensionType> dimType ) { super( dimType.getRegistryName() ); }
public DimensionTypeEnvironment( RegistryKey<DimensionType> dimType, boolean invert ) { super( dimType.getRegistryName(), invert ); }
public DimensionTypeEnvironment( 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_DIMENSION_TYPE; }
/** @return The registry used. */
@Override
public RegistryKey<Registry<DimensionType>> getRegistry() { return Registry.DIMENSION_TYPE_REGISTRY; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public boolean matches( ServerWorld world, @Nullable BlockPos pos ) {
final DimensionType entry = getRegistryEntry( world );
return (entry != null && entry.equals( world.dimensionType() )) != INVERT;
}
}

View file

@ -0,0 +1,41 @@
package fathertoast.specialmobs.common.config.util.environment.dimension;
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.math.BlockPos;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.DimensionType;
import net.minecraft.world.server.ServerWorld;
import javax.annotation.Nullable;
import java.util.List;
public class DimensionTypeGroupEnvironment extends DynamicRegistryGroupEnvironment<DimensionType> {
public DimensionTypeGroupEnvironment( RegistryKey<DimensionType> dimType ) { super( dimType.getRegistryName() ); }
public DimensionTypeGroupEnvironment( RegistryKey<DimensionType> dimType, boolean invert ) { super( dimType.getRegistryName(), invert ); }
public DimensionTypeGroupEnvironment( 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_DIMENSION_TYPE; }
/** @return The registry used. */
@Override
public RegistryKey<Registry<DimensionType>> getRegistry() { return Registry.DIMENSION_TYPE_REGISTRY; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public final boolean matches( ServerWorld world, @Nullable BlockPos pos ) {
final DimensionType target = world.dimensionType();
final List<DimensionType> entries = getRegistryEntries( world );
for( DimensionType entry : entries ) {
if( entry.equals( target ) ) return !INVERT;
}
return INVERT;
}
}

View file

@ -0,0 +1,7 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package fathertoast.specialmobs.common.config.util.environment.dimension;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -0,0 +1,7 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package fathertoast.specialmobs.common.config.util.environment;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -0,0 +1,38 @@
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.RegistryEnvironment;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.structure.Structure;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.IForgeRegistry;
import javax.annotation.Nullable;
public class StructureEnvironment extends RegistryEnvironment<Structure<?>> {
public StructureEnvironment( Structure<?> structure ) { super( structure ); }
public StructureEnvironment( Structure<?> structure, boolean invert ) { super( structure, invert ); }
public StructureEnvironment( 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_STRUCTURE; }
/** @return The registry used. */
@Override
public IForgeRegistry<Structure<?>> getRegistry() { return ForgeRegistries.STRUCTURE_FEATURES; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public boolean matches( World world, @Nullable BlockPos pos ) {
final Structure<?> entry = getRegistryEntry();
return (entry != null && pos != null && world instanceof ServerWorld &&
((ServerWorld) world).structureFeatureManager().getStructureAt( pos, false, entry ).isValid()) != INVERT;
}
}

View file

@ -0,0 +1,46 @@
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.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.structure.Structure;
import net.minecraft.world.gen.feature.structure.StructureManager;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.IForgeRegistry;
import javax.annotation.Nullable;
import java.util.List;
public class StructureGroupEnvironment extends RegistryGroupEnvironment<Structure<?>> {
public StructureGroupEnvironment( Structure<?> biome ) { super( biome ); }
public StructureGroupEnvironment( Structure<?> biome, boolean invert ) { super( biome, invert ); }
public StructureGroupEnvironment( 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_STRUCTURE; }
/** @return The registry used. */
@Override
public IForgeRegistry<Structure<?>> getRegistry() { return ForgeRegistries.STRUCTURE_FEATURES; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public final boolean matches( World world, @Nullable BlockPos pos ) {
final StructureManager structureManager = pos != null && world instanceof ServerWorld ?
((ServerWorld) world).structureFeatureManager() : null;
if( structureManager != null ) {
final List<Structure<?>> entries = getRegistryEntries();
for( Structure<?> entry : entries ) {
if( structureManager.getStructureAt( pos, false, entry ).isValid() ) return !INVERT;
}
}
return INVERT;
}
}

View file

@ -0,0 +1,25 @@
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.CompareIntEnvironment;
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 YEnvironment extends CompareIntEnvironment {
public YEnvironment( ComparisonOperator op, int value ) { super( op, value ); }
public YEnvironment( 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_Y; }
/** @return Returns the actual value to compare, or null if there isn't enough information. */
@Override
public Integer getActual( World world, @Nullable BlockPos pos ) { return pos == null ? null : pos.getY(); }
}

View file

@ -0,0 +1,25 @@
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.CompareIntEnvironment;
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 YFromSeaEnvironment extends CompareIntEnvironment {
public YFromSeaEnvironment( ComparisonOperator op, int value ) { super( op, value ); }
public YFromSeaEnvironment( 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_Y_FROM_SEA; }
/** @return Returns the actual value to compare, or null if there isn't enough information. */
@Override
public Integer getActual( World world, @Nullable BlockPos pos ) { return pos == null ? null : pos.getY() - world.getSeaLevel(); }
}

View file

@ -0,0 +1,7 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package fathertoast.specialmobs.common.config.util.environment.position;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -0,0 +1,45 @@
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.EnumEnvironment;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nullable;
public class DayTimeEnvironment extends EnumEnvironment<DayTimeEnvironment.Value> {
/** Values match up to the vanilla set time command. */
public enum Value {
DAY( 1_000, 13_000 ), SUNSET( 12_000, 13_000 ),
NIGHT( 13_000, 1_000 ), SUNRISE( 23_000, 1_000 );
private final int START, END;
Value( int start, int end ) {
START = start;
END = end;
}
public boolean matches( int dayTime ) {
if( START < END ) return START <= dayTime && dayTime < END;
return START <= dayTime || dayTime < END; // Handle day wrapping
}
}
public DayTimeEnvironment( Value value ) { super( value ); }
public DayTimeEnvironment( Value value, boolean invert ) { super( value, invert ); }
public DayTimeEnvironment( AbstractConfigField field, String line ) { super( field, line, Value.values() ); }
/** @return The string name of this environment, as it would appear in a config file. */
@Override
public String name() { return EnvironmentListField.ENV_DAY_TIME; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public boolean matches( World world, @Nullable BlockPos pos ) {
return (VALUE.matches( (int) (world.dayTime() / 24_000L) )) != INVERT;
}
}

View file

@ -0,0 +1,40 @@
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.CompareFloatEnvironment;
import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nullable;
/**
* Notes on regional difficulty:
* Maxes out over 63 days in the world and 150 days the in the chunk (effectively, time the chunk has been loaded).
* Peaks during the full moon and dramatically scaled by difficulty setting.
* <p>
* Peaceful: 0
* Easy: 0.75 to 1.5 (0.25 from world time, 0.375 from chunk time, and 0.125 from moon brightness)
* Normal: 1.5 to 4.0 (0.5 from world time, 1.5 from chunk time, and 0.5 from moon brightness)
* Hard: 2.25 to 6.75 (0.75 from world time, 3.0 from chunk time, and 0.75 from moon brightness)
*/
public class DifficultyEnvironment extends CompareFloatEnvironment {
public DifficultyEnvironment( ComparisonOperator op, float value ) { super( op, value ); }
public DifficultyEnvironment( AbstractConfigField field, String line ) { super( field, line ); }
/** @return The minimum value that can be given to the value. */
protected float getMinValue() { return 0.0F; }
/** @return The string name of this environment, as it would appear in a config file. */
@Override
public String name() { return EnvironmentListField.ENV_DIFFICULTY; }
/** @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 ) {
return pos == null ? Float.NaN : world.getCurrentDifficultyAt( pos ).getEffectiveDifficulty();
}
}

View file

@ -0,0 +1,34 @@
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.CompareFloatEnvironment;
import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.DimensionType;
import net.minecraft.world.World;
import javax.annotation.Nullable;
public class MoonBrightnessEnvironment extends CompareFloatEnvironment {
public MoonBrightnessEnvironment( ComparisonOperator op, float value ) { super( op, value ); }
public MoonBrightnessEnvironment( AbstractConfigField field, String line ) { super( field, line ); }
/** @return The minimum value that can be given to the value. */
protected float getMinValue() { return 0.0F; }
/** @return The maximum value that can be given to the value. */
protected float getMaxValue() { return 1.0F; }
/** @return The string name of this environment, as it would appear in a config file. */
@Override
public String name() { return EnvironmentListField.ENV_MOON_BRIGHTNESS; }
/** @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 ) {
return pos == null ? Float.NaN : DimensionType.MOON_BRIGHTNESS_PER_PHASE[world.dimensionType().moonPhase( world.dayTime() )];
}
}

View file

@ -0,0 +1,38 @@
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.EnumEnvironment;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nullable;
public class MoonPhaseEnvironment extends EnumEnvironment<MoonPhaseEnvironment.Value> {
/** Values match up to the vanilla weather command. */
public enum Value {
FULL( 0 ), WANING_GIBBOUS( 1 ), LAST_QUARTER( 2 ), WANING_CRESCENT( 3 ),
NEW( 4 ), WAXING_CRESCENT( 5 ), FIRST_QUARTER( 6 ), WAXING_GIBBOUS( 7 );
public final int INDEX;
Value( int i ) { INDEX = i; }
}
public MoonPhaseEnvironment( Value value ) { super( value ); }
public MoonPhaseEnvironment( Value value, boolean invert ) { super( value, invert ); }
public MoonPhaseEnvironment( AbstractConfigField field, String line ) { super( field, line, Value.values() ); }
/** @return The string name of this environment, as it would appear in a config file. */
@Override
public String name() { return EnvironmentListField.ENV_MOON_PHASE; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public boolean matches( World world, @Nullable BlockPos pos ) {
final int phase = world.dimensionType().moonPhase( world.dayTime() );
return (VALUE.INDEX == phase) != INVERT;
}
}

View file

@ -0,0 +1,40 @@
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.CompareFloatEnvironment;
import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nullable;
/**
* Notes on special multiplier difficulty:
* This is 0 while regional difficulty is <= 2 and this is 1 while regional difficulty is >= 4 (linearly scales between).
* <p>
* In Peaceful and Easy, this is always 0. In Normal, this only maxes out at the absolute peak regional difficulty.
* In Hard, this starts out as 0.125 and reaches 1 during new moons with only ~50 days in the area.
*/
public class SpecialDifficultyEnvironment extends CompareFloatEnvironment {
public SpecialDifficultyEnvironment( ComparisonOperator op, float value ) { super( op, value ); }
public SpecialDifficultyEnvironment( AbstractConfigField field, String line ) { super( field, line ); }
/** @return The minimum value that can be given to the value. */
protected float getMinValue() { return 0.0F; }
/** @return The maximum value that can be given to the value. */
protected float getMaxValue() { return 1.0F; }
/** @return The string name of this environment, as it would appear in a config file. */
@Override
public String name() { return EnvironmentListField.ENV_SPECIAL_DIFFICULTY; }
/** @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 ) {
return pos == null ? Float.NaN : world.getCurrentDifficultyAt( pos ).getSpecialMultiplier();
}
}

View file

@ -0,0 +1,37 @@
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.CompareIntEnvironment;
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 TimeFromMidnightEnvironment extends CompareIntEnvironment {
public TimeFromMidnightEnvironment( ComparisonOperator op, int value ) { super( op, value ); }
public TimeFromMidnightEnvironment( 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_TIME_FROM_MIDNIGHT; }
/** @return The minimum value that can be given to the value. */
@Override
protected int getMinValue() { return 0; }
/** @return The maximum value that can be given to the value. */
@Override
protected int getMaxValue() { return 12_000; }
/** @return Returns the actual value to compare, or null if there isn't enough information. */
@Override
public Integer getActual( World world, @Nullable BlockPos pos ) {
int dayTime = (int) (world.dayTime() / 24_000L);
if( dayTime < 18_000 ) dayTime += 24_000;
return dayTime - 18_000;
}
}

View file

@ -0,0 +1,32 @@
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.EnumEnvironment;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nullable;
public class WeatherEnvironment extends EnumEnvironment<WeatherEnvironment.Value> {
/** Values match up to the vanilla weather command. */
public enum Value { CLEAR, RAIN, THUNDER }
public WeatherEnvironment( Value value ) { super( value ); }
public WeatherEnvironment( Value value, boolean invert ) { super( value, invert ); }
public WeatherEnvironment( AbstractConfigField field, String line ) { super( field, line, Value.values() ); }
/** @return The string name of this environment, as it would appear in a config file. */
@Override
public String name() { return EnvironmentListField.ENV_BIOME_CATEGORY; }
/** @return Returns true if this environment matches the provided environment. */
@Override
public boolean matches( World world, @Nullable BlockPos pos ) {
if( world.getLevelData().isThundering() ) return (VALUE == Value.CLEAR) == INVERT; // Thunder implies rain
if( world.getLevelData().isRaining() ) return (VALUE == Value.RAIN) != INVERT;
return (VALUE == Value.CLEAR) != INVERT;
}
}

View file

@ -0,0 +1,29 @@
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 WorldTimeEnvironment extends CompareLongEnvironment {
public WorldTimeEnvironment( ComparisonOperator op, long value ) { super( op, value ); }
public WorldTimeEnvironment( 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_MOON_BRIGHTNESS; }
/** @return Returns the actual value to compare, or null if there isn't enough information. */
@Override
public Long getActual( World world, @Nullable BlockPos pos ) { return world.dayTime(); }
}

View file

@ -0,0 +1,7 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package fathertoast.specialmobs.common.config.util.environment.time;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -0,0 +1,7 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package fathertoast.specialmobs.common.config.util;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -4,7 +4,6 @@ import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.entity.MobHelper;
import fathertoast.specialmobs.common.util.References;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.nbt.CompoundNBT;
@ -18,12 +17,9 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.ArrayDeque;
import java.util.Deque;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@Mod.EventBusSubscriber( modid = SpecialMobs.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE )
public final class SpecialMobReplacer {
/** List of data for mobs needing replacement. */

View file

@ -0,0 +1,7 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package fathertoast.specialmobs.common.core;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -0,0 +1,7 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
package fathertoast.specialmobs.common.core.register;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;