Environment-sensitive special chance!

This commit is contained in:
FatherToast 2022-07-17 22:55:12 -05:00
parent 999d9b3edf
commit 74a106ba6a
38 changed files with 520 additions and 170 deletions

View file

@ -24,6 +24,7 @@ public class Config {
/** Performs initial loading of all configs in this mod. */
public static void initialize() {
ToastConfigSpec.freezeFileWatcher = true;
ReadMeConfig.makeReadMe( CONFIG_DIR );
MAIN.SPEC.initialize();
MobFamily.initBestiary();
ToastConfigSpec.freezeFileWatcher = false;

View file

@ -15,20 +15,6 @@ public class MainConfig extends Config.AbstractConfig {
super( dir, fileName,
"This config contains options that apply to the mod as a whole, including some master settings",
"toggles for convenience." );
SPEC.newLine();
SPEC.comment(
"Terminology used in Special Mobs configs:",
" * Mob - An entity that is 'alive', short for \"Mobile\" or MobEntity.",
" * Family - The group of mobs based on (but not including) a particular vanilla mob; e.g., Creepers.",
" * Species - A specific type of mob within a family; e.g., Fire Creepers or vanilla-replacement Creepers.",
" * Vanilla Replacement - The one species within a family that is intended to be a replica of the base vanilla mob.",
" * Special Variant - Any species that is not the family's vanilla replacement. Includes species that are",
" replicas of 'vanilla special variants'; i.e. Husks and Strays.",
" * Mob Replacer - The tool that watches vanilla mob spawns and cancels them to spawn this mod's entities." );
SPEC.newLine();
SPEC.describeAttributeList();
SPEC.newLine();
SPEC.describeRegistryEntryList();
GENERAL = new General( SPEC );
}

View file

@ -0,0 +1,36 @@
package fathertoast.specialmobs.common.config;
import fathertoast.specialmobs.common.config.field.BooleanField;
import java.io.File;
public class ReadMeConfig extends Config.AbstractConfig {
@SuppressWarnings( "SameParameterValue" )
static void makeReadMe( File dir ) { new ReadMeConfig( dir ).SPEC.initialize(); }
/** Builds the config spec that should be used for this config. */
private ReadMeConfig( File dir ) {
super( dir, "__README__", "This file contains helpful information about how to use the config files in this mod." );
SPEC.newLine( 2 );
SPEC.comment(
"Terminology used in Special Mobs configs:",
" * Mob - An entity that is 'alive', short for \"Mobile\" or MobEntity.",
" * Family - The group of mobs based on (but not including) a particular vanilla mob; e.g., Creepers.",
" * Species - A specific type of mob within a family; e.g., Fire Creepers or vanilla-replacement Creepers.",
" * Vanilla Replacement - The one species within a family that is intended to be a replica of the base vanilla mob.",
" * Special Variant - Any species that is not the family's vanilla replacement. Includes species that are",
" replicas of 'vanilla special variants'; i.e. Husks and Strays.",
" * Mob Replacer - The tool that watches vanilla mob spawns and cancels them to spawn this mod's entities." );
SPEC.newLine( 2 );
SPEC.describeAttributeList();
SPEC.newLine( 2 );
SPEC.describeRegistryEntryList();
SPEC.newLine( 2 );
SPEC.describeEnvironmentListPart1of2();
SPEC.newLine();
SPEC.describeEnvironmentListPart2of2();
SPEC.newLine( 4 );
SPEC.define( new BooleanField( "secret_mode", false, (String[]) null ) );
}
}

View file

@ -4,10 +4,13 @@ import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.config.Config;
import fathertoast.specialmobs.common.config.field.BooleanField;
import fathertoast.specialmobs.common.config.field.DoubleField;
import fathertoast.specialmobs.common.config.field.EnvironmentListField;
import fathertoast.specialmobs.common.config.field.IntField;
import fathertoast.specialmobs.common.config.file.ToastConfigSpec;
import fathertoast.specialmobs.common.config.file.TomlHelper;
import fathertoast.specialmobs.common.config.util.ConfigUtil;
import fathertoast.specialmobs.common.config.util.EnvironmentEntry;
import fathertoast.specialmobs.common.config.util.EnvironmentList;
import java.io.File;
import java.util.List;
@ -51,7 +54,7 @@ public class FamilyConfig extends Config.AbstractConfig {
public final DoubleField familyRandomScaling;
public final DoubleField specialVariantChance;
public final DoubleField.EnvironmentSensitive specialVariantChance;
public final IntField[] specialVariantWeights;
@ -72,9 +75,17 @@ public class FamilyConfig extends Config.AbstractConfig {
SPEC.newLine();
specialVariantChance = SPEC.define( new DoubleField( "special_variant_chance", variantChance, DoubleField.Range.PERCENT,
"The chance for " + family.configName + " to spawn as special variants." ) );
// TODO special variant chance exceptions
specialVariantChance = new DoubleField.EnvironmentSensitive(
SPEC.define( new DoubleField( "special_variant_chance.base", variantChance, DoubleField.Range.PERCENT,
"The chance for " + family.configName + " to spawn as special variants." ) ),
SPEC.define( new EnvironmentListField( "special_variant_chance.exceptions", new EnvironmentList(
EnvironmentEntry.builder( (float) variantChance * 0.5F ).beforeDays( 5 ).build(), // Also skips first night's full moon
EnvironmentEntry.builder( (float) variantChance * 2.0F ).atMaxMoonLight().aboveDifficulty( 0.5F ).build(),
EnvironmentEntry.builder( (float) variantChance * 1.5F ).atMaxMoonLight().build(),
EnvironmentEntry.builder( (float) variantChance * 1.5F ).aboveDifficulty( 0.5F ).build() )
.setRange( DoubleField.Range.PERCENT ),
"The chance for " + family.configName + " to spawn as special variants when specific environmental conditions are met." ) )
);
SPEC.newLine();

View file

@ -2,8 +2,12 @@ package fathertoast.specialmobs.common.config.field;
import fathertoast.specialmobs.common.config.file.TomlHelper;
import fathertoast.specialmobs.common.core.SpecialMobs;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
@ -156,4 +160,89 @@ public class DoubleField extends AbstractConfigField {
return getMin();
}
}
/**
* Represents a double field and an environment exception list, combined into one.
* This has convenience methods for returning the value that should be used based on the environment.
*/
public static class EnvironmentSensitive {
/** The base value. */
private final DoubleField BASE;
/** The environment exceptions list. */
private final EnvironmentListField EXCEPTIONS;
/** Links two fields together as base and exceptions. */
public EnvironmentSensitive( DoubleField base, EnvironmentListField exceptions ) {
BASE = base;
EXCEPTIONS = exceptions;
}
/** @return Returns the config field's value. */
public double get( World world, @Nullable BlockPos pos ) { return EXCEPTIONS.getOrElse( world, pos, BASE ); }
/** @return Treats the config field's value as a percent chance (from 0 to 1) and returns the result of a single roll. */
public boolean rollChance( Random random, World world, @Nullable BlockPos pos ) { return random.nextDouble() < get( world, pos ); }
}
/**
* Represents an environment sensitive list of weighted values. Unlike the normal weighted list, this is just a simple
* wrapper class, and its weights are doubles.
* It sacrifices automation for flexibility, largely to help with the craziness of environment list fields.
*/
public static class EnvironmentSensitiveWeightedList<T> {
private final List<Entry<T>> UNDERLYING_LIST;
/** Links an array of values to two arrays of fields as base weights and exceptions. */
public EnvironmentSensitiveWeightedList( T[] values, DoubleField[] baseWeights, EnvironmentListField[] weightExceptions ) {
if( values.length != baseWeights.length || values.length != weightExceptions.length )
throw new IllegalArgumentException( "All arrays must be equal length!" );
final ArrayList<Entry<T>> list = new ArrayList<>();
for( int i = 0; i < values.length; i++ ) {
list.add( new Entry<>( values[i], new EnvironmentSensitive( baseWeights[i], weightExceptions[i] ) ) );
// Do a bit of error checking; allows us to ignore the possibility of negative weights
if( baseWeights[i].valueMin < 0.0 || weightExceptions[i].valueDefault.getMinValue() < 0.0 ) {
throw new IllegalArgumentException( "Weight is not allowed to be negative! See " +
baseWeights[i].getKey() + " and/or " + weightExceptions[i].getKey() );
}
}
list.trimToSize();
UNDERLYING_LIST = Collections.unmodifiableList( list );
}
/** @return Returns a random item from this weighted list. Null if none of the items have a positive weight. */
@Nullable
public T next( Random random, World world, @Nullable BlockPos pos ) {
// Due to the 'nebulous' nature of environment-based weights, we must recalculate weights for EVERY call
final double[] weights = new double[UNDERLYING_LIST.size()];
double targetWeight = 0.0;
for( int i = 0; i < weights.length; i++ ) {
targetWeight += weights[i] = UNDERLYING_LIST.get( i ).WEIGHT.get( world, pos );
}
if( targetWeight <= 0.0 ) return null;
// Now we unravel the target weight to a random point
targetWeight *= random.nextDouble();
for( int i = 0; i < weights.length; i++ ) {
targetWeight -= weights[i];
if( targetWeight < 0.0 ) return UNDERLYING_LIST.get( i ).VALUE;
}
SpecialMobs.LOG.error( "Environment-sensitive weight list was unable to return a value when it should have! " +
"This is probably due to error in floating point calculations, perhaps try changing the scale of weights." );
return null;
}
private static class Entry<T> {
final T VALUE;
final DoubleField.EnvironmentSensitive WEIGHT;
Entry( T value, DoubleField.EnvironmentSensitive weight ) {
VALUE = value;
WEIGHT = weight;
}
}
}
}

View file

@ -17,6 +17,7 @@ import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* Represents a config field with an environment list value.
@ -53,6 +54,7 @@ public class EnvironmentListField extends GenericField<EnvironmentList> {
public static final String ENV_DAY_TIME = "day_time";
public static final String ENV_TIME_FROM_MIDNIGHT = "time_from_midnight";
public static final String ENV_WORLD_TIME = "world_time";
public static final String ENV_CHUNK_TIME = "chunk_time";
/**
* Provides a description of how to use environment lists. Recommended to put at the top of any file using environment lists.
@ -78,7 +80,7 @@ public class EnvironmentListField extends GenericField<EnvironmentList> {
comment.add( " \"" + ENV_DIMENSION_PROPERTY + " (!)property\":" );
comment.add( " Valid property values: " + TomlHelper.literalList( (Object[]) DimensionPropertyEnvironment.Value.values() ) );
comment.add( " Dimension properties are the true/false values available to dimension types in data packs." );
comment.add( " See the wiki for more info: [https://minecraft.fandom.com/wiki/Custom_dimension#Syntax]." );
comment.add( " See the wiki for more info: [https://minecraft.fandom.com/wiki/Custom_dimension#Syntax]." );
comment.add( " \"" + ENV_DIMENSION_TYPE + " (!)namespace:dimension_type_name\":" );
comment.add( " The world's dimension type. In vanilla, these are only \"minecraft:overworld\", \"minecraft:the_nether\", or \"minecraft:the_end\"." );
// Biome-based
@ -89,7 +91,7 @@ public class EnvironmentListField extends GenericField<EnvironmentList> {
comment.add( " \"" + ENV_TEMPERATURE + " op value\" or \"" + ENV_TEMPERATURE + " (!)" + TemperatureEnvironment.FREEZING + "\":" );
comment.add( " Height-adjusted temperature. For reference, freezing is < 0.15 and hot is generally considered > 0.95." );
comment.add( " \"" + ENV_BIOME_CATEGORY + " (!)category\":" );
comment.add( " Valid category values: " + TomlHelper.literalList( (Object[]) BiomeCategoryEnvironment.Value.values() ) );
comment.add( " Valid category values: " + TomlHelper.literalList( (Object[]) BiomeCategory.values() ) );
comment.add( " \"" + ENV_BIOME + " (!)namespace:biome_name\":" );
comment.add( " The biome. See the wiki for vanilla biome names (resource locations) [https://minecraft.fandom.com/wiki/Biome#Biome_IDs]." );
// Position-based
@ -98,7 +100,7 @@ public class EnvironmentListField extends GenericField<EnvironmentList> {
comment.add( " \"" + ENV_Y + " op value\":" );
comment.add( " The y-value. For reference, sea level is normally 63 and lava level is normally 10." );//TODO change lava level to -54 for MC 1.18
comment.add( " \"" + ENV_Y_FROM_SEA + " op value\":" );
comment.add( " The y-value from sea level. Expect the only air <= 0 to be in caves (which may still have direct view of the sky)." );
comment.add( " The y-value from sea level. Expect the only air <= 0 to be in caves/ravines (which may still have direct view of the sky)." );
comment.add( " \"" + ENV_POSITION + " (!)state\":" );
comment.add( " Valid state values: " + TomlHelper.literalList( (Object[]) PositionEnvironment.Value.values() ) );
comment.add( " Miscellaneous conditions that generally do what you expect. For reference, 'near' a village is ~3 chunks, and redstone checks weak power." );
@ -123,8 +125,11 @@ public class EnvironmentListField extends GenericField<EnvironmentList> {
comment.add( " The absolute time in ticks away from midnight. Value must be 0 to 12000." );
comment.add( " \"" + ENV_MOON_PHASE + " (!)phase\":" );
comment.add( " Valid phase values: " + TomlHelper.literalList( (Object[]) MoonPhaseEnvironment.Value.values() ) );
comment.add( " For reference, the first day in a new world is always a full moon." );
comment.add( " \"" + ENV_WORLD_TIME + " op value\":" );
comment.add( " The total time the world has existed, in ticks. For reference, each day cycle is 24000 ticks and each lunar cycle is 192000 ticks." );
comment.add( " \"" + ENV_CHUNK_TIME + " op value\":" );
comment.add( " The total time the chunk has been loaded, in ticks. For reference, each day cycle is 24000 ticks and each lunar cycle is 192000 ticks." );
return comment;
}
@ -167,7 +172,7 @@ public class EnvironmentListField extends GenericField<EnvironmentList> {
final List<AbstractEnvironment> conditions = new ArrayList<>();
if( args.length > 1 ) {
final String[] condArgs = args[1].trim().split( "&" );
final String[] condArgs = args[1].split( "&" );
for( String condArg : condArgs ) {
conditions.add( parseCondition( condArg.trim(), line ) );
}
@ -216,7 +221,7 @@ public class EnvironmentListField extends GenericField<EnvironmentList> {
if( args.length < 2 ) value = "";
else value = args[1].trim();
switch( args[0] ) {
switch( args[0].toLowerCase( Locale.ROOT ) ) {
// Dimension-based
case ENV_DIMENSION_PROPERTY:
return new DimensionPropertyEnvironment( this, value );
@ -259,6 +264,8 @@ public class EnvironmentListField extends GenericField<EnvironmentList> {
return new TimeFromMidnightEnvironment( this, value );
case ENV_WORLD_TIME:
return new WorldTimeEnvironment( this, value );
case ENV_CHUNK_TIME:
return new ChunkTimeEnvironment( this, value );
}
// The environment name was not recognized; try to provide some good feedback because this field is complicated
@ -271,7 +278,7 @@ public class EnvironmentListField extends GenericField<EnvironmentList> {
ENV_STRUCTURE, ENV_Y, ENV_Y_FROM_SEA, ENV_POSITION,
// Time-based
ENV_DIFFICULTY, ENV_SPECIAL_DIFFICULTY, ENV_WEATHER, ENV_MOON_BRIGHTNESS, ENV_MOON_PHASE, ENV_DAY_TIME,
ENV_TIME_FROM_MIDNIGHT, ENV_WORLD_TIME
ENV_TIME_FROM_MIDNIGHT, ENV_WORLD_TIME, ENV_CHUNK_TIME
};
final AbstractEnvironment fallback = new YEnvironment( ComparisonOperator.LESS_THAN, 0 );
SpecialMobs.LOG.warn( "Invalid environment '{}' for {} \"{}\"! Falling back to \"{}\". Environment name must be in the set [ {} ]. Invalid environment: {}",

View file

@ -227,6 +227,30 @@ public class ToastConfigSpec {
}
}
/** Represents an appendix header comment. */
private static class AppendixHeader extends Format {
/** The appendix comment. */
private final List<String> COMMENT;
/** Create a new header action that will insert the opening file comment. */
private AppendixHeader( List<String> comment ) {
COMMENT = comment;
}
/** Called when the config is saved. */
@Override
public void write( ToastTomlWriter writer, CharacterOutput output ) {
writer.decreaseIndentLevel();
writer.writeNewLine( output );
writer.writeNewLine( output );
writer.writeComment( "Appendix:", output );
writer.writeComment( COMMENT, output );
writer.increaseIndentLevel();
}
}
/** Represents a category comment. */
private static class Category extends Format {
/** The category comment. */
@ -355,6 +379,9 @@ public class ToastConfigSpec {
/** @param comment The file comment to insert. */
public void header( List<String> comment ) { ACTIONS.add( new Header( this, comment ) ); }
/** @param comment The appendix comment to insert. */
public void appendixHeader( String... comment ) { ACTIONS.add( new AppendixHeader( TomlHelper.newComment( comment ) ) ); }
/** Inserts a detailed description of how to use the registry entry list field. */
public void describeRegistryEntryList() { ACTIONS.add( new Comment( RegistryEntryListField.verboseDescription() ) ); }
@ -367,6 +394,12 @@ public class ToastConfigSpec {
/** Inserts a detailed description of how to use the block list field. */
public void describeBlockList() { ACTIONS.add( new Comment( BlockListField.verboseDescription() ) ); }
/** Inserts the first part of a detailed description of how to use the environment list field. Should go with the other field descriptions. */
public void describeEnvironmentListPart1of2() { ACTIONS.add( new Comment( EnvironmentListField.verboseDescription() ) ); }
/** Inserts the second and last part of a detailed description of how to use the environment list field. Should go at the bottom of the file. */
public void describeEnvironmentListPart2of2() { ACTIONS.add( new Comment( EnvironmentListField.environmentDescriptions() ) ); }
/**
* @param name The category name.
* @param comment The category comment to insert.

View file

@ -5,6 +5,7 @@ import com.electronwill.nightconfig.core.io.*;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.core.SpecialMobs;
import javax.annotation.Nullable;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
@ -85,7 +86,7 @@ public class ToastTomlWriter implements ConfigWriter {
}
/** Writes a literal array of single-line strings. */
public void writeStringArray( List<String> list, CharacterOutput output ) {
public void writeStringArray( @Nullable List<String> list, CharacterOutput output ) {
if( list == null || list.isEmpty() ) {
writeLine( "[]", output );
return;

View file

@ -38,8 +38,6 @@ public class SpeciesConfig extends Config.AbstractConfig {
super( FamilyConfig.dir( species.family ), fileName( species ),
String.format( "This config contains options that apply only to the %s %s species.",
variantName( species ), ConfigUtil.camelCaseToLowerSpace( species.family.name ) ) );
SPEC.newLine();
SPEC.comment( "See the main mod config for common terms and how to use special field types like Attribute List or Registry List." );
speciesName = variantName( species ) + " " + species.family.configName;

View file

@ -1,16 +1,20 @@
package fathertoast.specialmobs.common.config.util;
import fathertoast.specialmobs.common.config.util.environment.*;
import fathertoast.specialmobs.common.config.util.environment.biome.BiomeEnvironment;
import fathertoast.specialmobs.common.config.util.environment.biome.*;
import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionPropertyEnvironment;
import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionTypeEnvironment;
import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionTypeGroupEnvironment;
import fathertoast.specialmobs.common.config.util.environment.position.PositionEnvironment;
import fathertoast.specialmobs.common.config.util.environment.position.StructureEnvironment;
import fathertoast.specialmobs.common.config.util.environment.position.YEnvironment;
import fathertoast.specialmobs.common.config.util.environment.position.YFromSeaEnvironment;
import fathertoast.specialmobs.common.config.util.environment.time.*;
import net.minecraft.util.RegistryKey;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.DimensionType;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.feature.structure.Structure;
import javax.annotation.Nullable;
@ -64,10 +68,20 @@ public class EnvironmentEntry {
return str.toString();
}
// Builder Implementation
/** Creates a new entry builder. The value is rounded to 2 decimal place precision. */
public static Builder builder( float value ) { return new Builder( Math.round( value * 100.0 ) / 100.0 ); }
/** Creates a new entry builder. */
public static Builder builder( double value ) { return new Builder( value ); }
/**
* Builder class used to simplify creation of environment entries, with shortcuts for the most commonly used environments.
* Builder class used to simplify creation of environment entries for default configs,
* with shortcuts for the most commonly used environments.
* <p>
* Keep in mind that ALL conditions in an entry must be satisfied for it to be chosen.
*/
public static class Builder {
private final double VALUE;
@ -85,28 +99,187 @@ public class EnvironmentEntry {
// Dimension-based
public Builder inDimensionType( RegistryKey<DimensionType> dimType ) { return in( new DimensionTypeEnvironment( dimType ) ); }
/** Check if the dimension type causes water to instantly vaporize and has faster lava flow. */
public Builder inUltraWarmDimension() { return inDimensionWithProperty( DimensionPropertyEnvironment.Value.ULTRAWARM, false ); }
/** Check if the dimension type causes water to instantly vaporize and has faster lava flow. */
public Builder notInUltraWarmDimension() { return inDimensionWithProperty( DimensionPropertyEnvironment.Value.ULTRAWARM, true ); }
/** Check if the dimension type allows clocks, compasses, and beds to work. */
public Builder inNaturalDimension() { return inDimensionWithProperty( DimensionPropertyEnvironment.Value.NATURAL, false ); }
/** Check if the dimension type allows clocks, compasses, and beds to work. */
public Builder notInNaturalDimension() { return inDimensionWithProperty( DimensionPropertyEnvironment.Value.NATURAL, true ); }
private Builder inDimensionWithProperty( DimensionPropertyEnvironment.Value property, boolean invert ) {
return in( new DimensionPropertyEnvironment( property, invert ) );
}
public Builder inOverworld() { return inDimensionType( DimensionType.OVERWORLD_LOCATION, false ); }
public Builder notInOverworld() { return inDimensionType( DimensionType.OVERWORLD_LOCATION, true ); }
public Builder inNether() { return inDimensionType( DimensionType.NETHER_LOCATION, false ); }
public Builder notInNether() { return inDimensionType( DimensionType.NETHER_LOCATION, true ); }
public Builder inTheEnd() { return inDimensionType( DimensionType.END_LOCATION, false ); }
public Builder notInTheEnd() { return inDimensionType( DimensionType.END_LOCATION, true ); }
private Builder inDimensionType( RegistryKey<DimensionType> dimType, boolean invert ) { return in( new DimensionTypeEnvironment( dimType, invert ) ); }
/** Check if the dimension type is vanilla (registered with the "minecraft" namespace). */
public Builder inVanillaDimension() { return in( new DimensionTypeGroupEnvironment( new ResourceLocation( "" ), false ) ); }
/** Check if the dimension type is vanilla (registered with the "minecraft" namespace). */
public Builder notInVanillaDimension() { return in( new DimensionTypeGroupEnvironment( new ResourceLocation( "" ), true ) ); }
// Biome-based
public Builder inBiome( RegistryKey<Biome> biome ) { return in( new BiomeEnvironment( biome ) ); }
/** Check if the biome has rain disabled. */
public Builder inDryBiome() { return inAvgRainfall( ComparisonOperator.EQUAL_TO, 0.0F ); }
/** Check if the biome has rain disabled. */
public Builder notInDryBiome() { return inAvgRainfall( ComparisonOperator.EQUAL_TO.invert(), 0.0F ); }
/** Check if the biome's humidity hinders fire spread. */
public Builder inHumidBiome() { return inAvgRainfall( ComparisonOperator.GREATER_THAN, 0.85F ); }
/** Check if the biome's humidity hinders fire spread. */
public Builder notInHumidBiome() { return inAvgRainfall( ComparisonOperator.GREATER_THAN.invert(), 0.85F ); }
private Builder inAvgRainfall( ComparisonOperator op, float value ) { return in( new RainfallEnvironment( op, value ) ); }
/** Check if the temperature is freezing. */
public Builder isFreezing() { return in( new TemperatureEnvironment( true ) ); }
/** Check if the temperature is freezing. */
public Builder isNotFreezing() { return in( new TemperatureEnvironment( false ) ); }
/** Check if the temperature is warm (disables snow golem trails). */
public Builder isWarm() { return isTemperature( ComparisonOperator.GREATER_OR_EQUAL, 0.8F ); }
/** Check if the temperature is warm (disables snow golem trails). */
public Builder isNotWarm() { return isTemperature( ComparisonOperator.GREATER_OR_EQUAL.invert(), 0.8F ); }
/** Check if the temperature is hot (causes snow golems to die). */
public Builder isHot() { return isTemperature( ComparisonOperator.GREATER_THAN, 1.0F ); }
/** Check if the temperature is hot (causes snow golems to die). */
public Builder isNotHot() { return isTemperature( ComparisonOperator.GREATER_THAN.invert(), 1.0F ); }
private Builder isTemperature( ComparisonOperator op, float value ) { return in( new TemperatureEnvironment( op, value ) ); }
/** Check if the biome belongs to a specific category. */
public Builder inBiomeCategory( BiomeCategory category ) { return in( new BiomeCategoryEnvironment( category, false ) ); }
/** Check if the biome belongs to a specific category. */
public Builder notInBiomeCategory( BiomeCategory category ) { return in( new BiomeCategoryEnvironment( category, true ) ); }
// Position-based
public Builder inStructure( Structure<?> structure ) { return in( new StructureEnvironment( structure ) ); }
/** Check if the position is inside a particular structure. See {@link Structure}. */
public Builder inStructure( Structure<?> structure ) { return in( new StructureEnvironment( structure, false ) ); }
public Builder belowY( int y ) { return in( new YEnvironment( ComparisonOperator.LESS_THAN, y ) ); }
/** Check if the position is inside a particular structure. See {@link Structure}. */
public Builder notInStructure( Structure<?> structure ) { return in( new StructureEnvironment( structure, true ) ); }
public Builder atLeastY( int y ) { return in( new YEnvironment( ComparisonOperator.GREATER_OR_EQUAL, y ) ); }
/** Check if diamond/redstone ore can generate at the position. */
public Builder belowDiamondLevel() { return belowY( 15 ); } // TODO update ore-based logic in 1.18
public Builder belowSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_THAN, 0 ) ); }
/** Check if diamond/redstone ore can generate at the position. */
public Builder aboveDiamondLevel() { return aboveY( 15 ); }
public Builder atLeastSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.GREATER_OR_EQUAL, 0 ) ); }
/** Check if gold/lapis ore can generate at the position. */
public Builder belowGoldLevel() { return belowY( 33 ); }
/** Check if gold/lapis ore can generate at the position. */
public Builder aboveGoldLevel() { return aboveY( 33 ); }
private Builder belowY( int y ) { return in( new YEnvironment( ComparisonOperator.LESS_OR_EQUAL, y ) ); }
private Builder aboveY( int y ) { return in( new YEnvironment( ComparisonOperator.LESS_OR_EQUAL.invert(), y ) ); }
/** Check if the position is above/below sea level. */
public Builder belowSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_OR_EQUAL, 0 ) ); }
/** Check if the position is above/below sea level. */
public Builder aboveSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_OR_EQUAL.invert(), 0 ) ); }
/** Check if the position is above/below the average sea floor. */
public Builder belowSeaFloor() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_OR_EQUAL, -18 ) ); }
/** Check if the position is above/below the average sea floor. */
public Builder aboveSeaFloor() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_OR_EQUAL.invert(), -18 ) ); }
public Builder canSeeSky() { return inPositionWithState( PositionEnvironment.Value.CAN_SEE_SKY, false ); }
public Builder cannotSeeSky() { return inPositionWithState( PositionEnvironment.Value.CAN_SEE_SKY, true ); }
public Builder isNearVillage() { return inPositionWithState( PositionEnvironment.Value.IS_NEAR_VILLAGE, false ); }
public Builder isNotNearVillage() { return inPositionWithState( PositionEnvironment.Value.IS_NEAR_VILLAGE, true ); }
public Builder isNearRaid() { return inPositionWithState( PositionEnvironment.Value.IS_NEAR_RAID, false ); }
public Builder isNotNearRaid() { return inPositionWithState( PositionEnvironment.Value.IS_NEAR_RAID, true ); }
private Builder inPositionWithState( PositionEnvironment.Value state, boolean invert ) { return in( new PositionEnvironment( state, invert ) ); }
// Time-based
/** Check if the special difficulty multiplier is above a threshold (0 - 1). */
public Builder aboveDifficulty( float percent ) { return in( new SpecialDifficultyEnvironment( ComparisonOperator.GREATER_OR_EQUAL, percent ) ); }
/** Check if the special difficulty multiplier is above a threshold (0 - 1). */
public Builder belowDifficulty( float percent ) { return in( new SpecialDifficultyEnvironment( ComparisonOperator.GREATER_OR_EQUAL.invert(), percent ) ); }
public Builder isRaining() { return inWeather( WeatherEnvironment.Value.RAIN, false ); } // same as "is not clear"
public Builder isNotRaining() { return inWeather( WeatherEnvironment.Value.RAIN, true ); } // same as "is clear"
public Builder isThundering() { return inWeather( WeatherEnvironment.Value.THUNDER, false ); }
public Builder isNotThundering() { return inWeather( WeatherEnvironment.Value.THUNDER, true ); }
private Builder inWeather( WeatherEnvironment.Value weather, boolean invert ) { return in( new WeatherEnvironment( weather, invert ) ); }
public Builder atMaxMoonLight() { return in( new MoonPhaseEnvironment( MoonPhaseEnvironment.Value.FULL, false ) ); }
public Builder aboveHalfMoonLight() { return fromHalfMoonLight( ComparisonOperator.GREATER_THAN ); }
public Builder atHalfMoonLight() { return fromHalfMoonLight( ComparisonOperator.EQUAL_TO ); }
public Builder belowHalfMoonLight() { return fromHalfMoonLight( ComparisonOperator.LESS_THAN ); }
public Builder atNoMoonLight() { return in( new MoonPhaseEnvironment( MoonPhaseEnvironment.Value.NEW, true ) ); }
private Builder fromHalfMoonLight( ComparisonOperator op ) { return in( new MoonBrightnessEnvironment( op, 0.5F ) ); }
public Builder isNight() { return in( new DayTimeEnvironment( DayTimeEnvironment.Value.NIGHT, false ) ); }
public Builder isDay() { return in( new DayTimeEnvironment( DayTimeEnvironment.Value.DAY, false ) ); }
/** Check if the time is during a quarter of the night centered on midnight. */
public Builder isNearMidnight() { return in( new TimeFromMidnightEnvironment( ComparisonOperator.LESS_OR_EQUAL, 1_500 ) ); }
/** Check if the time is during a quarter of the night centered on midnight. */
public Builder isNotNearMidnight() { return in( new TimeFromMidnightEnvironment( ComparisonOperator.LESS_OR_EQUAL.invert(), 1_500 ) ); }
/** Check if the world time is after a certain number of days. */
public Builder afterDays( int days ) { return in( new WorldTimeEnvironment( ComparisonOperator.GREATER_OR_EQUAL, 24_000L * days ) ); }
/** Check if the world time is after a certain number of days. */
public Builder beforeDays( int days ) { return in( new WorldTimeEnvironment( ComparisonOperator.GREATER_OR_EQUAL.invert(), 24_000L * days ) ); }
/** Check if the chunk inhabited time is after a certain number of days. */
public Builder afterDaysInChunk( int days ) { return in( new ChunkTimeEnvironment( ComparisonOperator.GREATER_OR_EQUAL, 24_000L * days ) ); }
/** Check if the chunk inhabited time is after a certain number of days. */
public Builder beforeDaysInChunk( int days ) { return in( new ChunkTimeEnvironment( ComparisonOperator.GREATER_OR_EQUAL.invert(), 24_000L * days ) ); }
}
}

View file

@ -15,9 +15,6 @@ import java.util.*;
*/
@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;
/** The weighted entries in this list. */
private final List<Entry<T>> ENTRIES;
/** The total weight of all entries in this list. */
@ -26,20 +23,20 @@ public class WeightedList<T extends WeightedList.Value> {
/**
* Creates a new weighted list config option and registers it and any needed definitions with the spec.
*/
public WeightedList( ToastConfigSpec parent, String key, T[] values, String... description ) {
this( parent, key, Arrays.asList( values ), description );
public WeightedList( ToastConfigSpec SPEC, String key, T[] values, @Nullable String... description ) {
this( SPEC, key, Arrays.asList( values ), description );
}
/**
* Creates a new weighted list config option and registers it and any needed definitions with the spec.
*/
public WeightedList( ToastConfigSpec parent, String key, Iterable<T> values, String... description ) {
SPEC = parent;
public WeightedList( ToastConfigSpec SPEC, String key, Iterable<T> values, @Nullable String... description ) {
final IntField.Range fieldRange = IntField.Range.NON_NEGATIVE;
List<String> comment = TomlHelper.newComment( description );
comment.add( TomlHelper.multiFieldInfo( fieldRange ) );
SPEC.comment( comment );
if( description != null ) {
List<String> comment = TomlHelper.newComment( description );
comment.add( TomlHelper.multiFieldInfo( fieldRange ) );
SPEC.comment( comment );
}
// Define each value's weight field and connect the value to its weight in an entry
List<Entry<T>> list = new ArrayList<>();

View file

@ -13,56 +13,54 @@ public enum ComparisonOperator {
@Override
public String toString() { return LITERAL; }
/** @return A convenience method that returns the opposite comparison operator only if the passed value is true. */
public ComparisonOperator invert( boolean invert ) { return invert ? invert() : this; }
/** @return The opposite comparison operator. */
public ComparisonOperator invert() {
switch( this ) {
case LESS_THAN: return GREATER_OR_EQUAL;
case LESS_OR_EQUAL: return GREATER_THAN;
case GREATER_THAN: return LESS_OR_EQUAL;
case GREATER_OR_EQUAL: return LESS_THAN;
case EQUAL_TO: return NOT_EQUAL_TO;
case NOT_EQUAL_TO: return EQUAL_TO;
}
throw new IllegalStateException( "Inversion implementation is invalid! :(" );
}
public boolean apply( float first, float second ) {
switch( this ) {
case LESS_THAN:
return first < second;
case LESS_OR_EQUAL:
return first <= second;
case GREATER_THAN:
return first > second;
case GREATER_OR_EQUAL:
return first >= second;
case EQUAL_TO:
return first == second;
case NOT_EQUAL_TO:
return first != second;
case LESS_THAN: return first < second;
case LESS_OR_EQUAL: return first <= second;
case GREATER_THAN: return first > second;
case GREATER_OR_EQUAL: return first >= second;
case EQUAL_TO: return first == second;
case NOT_EQUAL_TO: return first != second;
}
throw new IllegalStateException( "Float comparison implementation is invalid! :(" );
}
public boolean apply( int first, int second ) {
switch( this ) {
case LESS_THAN:
return first < second;
case LESS_OR_EQUAL:
return first <= second;
case GREATER_THAN:
return first > second;
case GREATER_OR_EQUAL:
return first >= second;
case EQUAL_TO:
return first == second;
case NOT_EQUAL_TO:
return first != second;
case LESS_THAN: return first < second;
case LESS_OR_EQUAL: return first <= second;
case GREATER_THAN: return first > second;
case GREATER_OR_EQUAL: return first >= second;
case EQUAL_TO: return first == second;
case NOT_EQUAL_TO: return first != second;
}
throw new IllegalStateException( "Integer comparison implementation is invalid! :(" );
}
public boolean apply( long first, long second ) {
switch( this ) {
case LESS_THAN:
return first < second;
case LESS_OR_EQUAL:
return first <= second;
case GREATER_THAN:
return first > second;
case GREATER_OR_EQUAL:
return first >= second;
case EQUAL_TO:
return first == second;
case NOT_EQUAL_TO:
return first != second;
case LESS_THAN: return first < second;
case LESS_OR_EQUAL: return first <= second;
case GREATER_THAN: return first > second;
case GREATER_OR_EQUAL: return first >= second;
case EQUAL_TO: return first == second;
case NOT_EQUAL_TO: return first != second;
}
throw new IllegalStateException( "Long comparison implementation is invalid! :(" );
}

View file

@ -28,8 +28,6 @@ public abstract class DynamicRegistryEnvironment<T> extends AbstractEnvironment
/** The value of ConfigUtil#DYNAMIC_REGISTRY_VERSION at the time of last poll. */
private byte version = -1;
public DynamicRegistryEnvironment( ResourceLocation regKey ) { this( regKey, false ); }
public DynamicRegistryEnvironment( ResourceLocation regKey, boolean invert ) {
FIELD = null;
INVERT = invert;

View file

@ -31,8 +31,6 @@ public abstract class DynamicRegistryGroupEnvironment<T> extends AbstractEnviron
/** The value of ConfigUtil#DYNAMIC_REGISTRY_VERSION at the time of last poll. */
private byte version = -1;
public DynamicRegistryGroupEnvironment( ResourceLocation regKey ) { this( regKey, false ); }
public DynamicRegistryGroupEnvironment( ResourceLocation regKey, boolean invert ) {
FIELD = null;
INVERT = invert;

View file

@ -12,8 +12,6 @@ public abstract class EnumEnvironment<T extends Enum<T>> extends AbstractEnviron
/** The enum value for this environment. */
protected final T VALUE;
public EnumEnvironment( T value ) { this( value, false ); }
public EnumEnvironment( T value, boolean invert ) {
INVERT = invert;
VALUE = value;

View file

@ -22,8 +22,6 @@ public abstract class RegistryEnvironment<T extends IForgeRegistryEntry<T>> exte
private T registryEntry;
public RegistryEnvironment( T regEntry ) { this( regEntry, false ); }
public RegistryEnvironment( T regEntry, boolean invert ) {
FIELD = null;
INVERT = invert;

View file

@ -24,13 +24,15 @@ public abstract class RegistryGroupEnvironment<T extends IForgeRegistryEntry<T>>
private List<T> registryEntries;
public RegistryGroupEnvironment( T regEntry ) { this( regEntry, false ); }
public RegistryGroupEnvironment( T regEntry, boolean invert ) {
//noinspection ConstantConditions
this( regEntry.getRegistryName(), invert );
}
public RegistryGroupEnvironment( ResourceLocation regKey, boolean invert ) {
FIELD = null;
INVERT = invert;
//noinspection ConstantConditions
NAMESPACE = regEntry.getRegistryName().toString();
NAMESPACE = regKey.toString();
}
public RegistryGroupEnvironment( AbstractConfigField field, String line ) {

View file

@ -0,0 +1,31 @@
package fathertoast.specialmobs.common.config.util.environment.biome;
import net.minecraft.world.biome.Biome;
/**
* Used to wrap the vanilla enum Biome.Category so that it can be safely used in configs.
* The declared names should match the string passed into vanilla enums' constructors so that both enums serialize identically.
*/
public enum BiomeCategory {
NONE( Biome.Category.NONE ),
TAIGA( Biome.Category.TAIGA ),
EXTREME_HILLS( Biome.Category.EXTREME_HILLS ),
JUNGLE( Biome.Category.JUNGLE ),
MESA( Biome.Category.MESA ),
PLAINS( Biome.Category.PLAINS ),
SAVANNA( Biome.Category.SAVANNA ),
ICY( Biome.Category.ICY ),
THE_END( Biome.Category.THEEND ),
BEACH( Biome.Category.BEACH ),
FOREST( Biome.Category.FOREST ),
OCEAN( Biome.Category.OCEAN ),
DESERT( Biome.Category.DESERT ),
RIVER( Biome.Category.RIVER ),
SWAMP( Biome.Category.SWAMP ),
MUSHROOM( Biome.Category.MUSHROOM ),
NETHER( Biome.Category.NETHER );
public final Biome.Category BASE;
BiomeCategory( Biome.Category base ) { BASE = base; }
}

View file

@ -5,40 +5,14 @@ import fathertoast.specialmobs.common.config.field.EnvironmentListField;
import fathertoast.specialmobs.common.config.util.environment.EnumEnvironment;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import javax.annotation.Nullable;
public class BiomeCategoryEnvironment extends EnumEnvironment<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 class BiomeCategoryEnvironment extends EnumEnvironment<BiomeCategory> {
public BiomeCategoryEnvironment( Value value ) { super( value ); }
public BiomeCategoryEnvironment( BiomeCategory value, boolean invert ) { super( value, invert ); }
public BiomeCategoryEnvironment( Value value, boolean invert ) { super( value, invert ); }
public BiomeCategoryEnvironment( AbstractConfigField field, String line ) { super( field, line, Value.values() ); }
public BiomeCategoryEnvironment( AbstractConfigField field, String line ) { super( field, line, BiomeCategory.values() ); }
/** @return The string name of this environment, as it would appear in a config file. */
@Override

View file

@ -13,8 +13,6 @@ 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 ); }

View file

@ -4,6 +4,7 @@ import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.config.field.EnvironmentListField;
import fathertoast.specialmobs.common.config.util.environment.DynamicRegistryGroupEnvironment;
import net.minecraft.util.RegistryKey;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
@ -14,9 +15,9 @@ import java.util.List;
public class BiomeGroupEnvironment extends DynamicRegistryGroupEnvironment<Biome> {
public BiomeGroupEnvironment( RegistryKey<Biome> biome ) { super( biome.getRegistryName() ); }
public BiomeGroupEnvironment( RegistryKey<Biome> biome, boolean invert ) { this( biome.getRegistryName(), invert ); }
public BiomeGroupEnvironment( RegistryKey<Biome> biome, boolean invert ) { super( biome.getRegistryName(), invert ); }
public BiomeGroupEnvironment( ResourceLocation regKey, boolean invert ) { super( regKey, invert ); }
public BiomeGroupEnvironment( AbstractConfigField field, String line ) { super( field, line ); }

View file

@ -2,33 +2,24 @@ package fathertoast.specialmobs.common.config.util.environment.biome;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.config.field.EnvironmentListField;
import fathertoast.specialmobs.common.config.util.environment.CompareFloatEnvironment;
import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nullable;
public class BiomeTemperatureEnvironment extends CompareFloatEnvironment {
public class BiomeTemperatureEnvironment extends TemperatureEnvironment {
public BiomeTemperatureEnvironment( boolean freezing ) { super( freezing ); }
public BiomeTemperatureEnvironment( ComparisonOperator op, float value ) { super( op, value ); }
public BiomeTemperatureEnvironment( AbstractConfigField field, String line ) { super( field, TemperatureEnvironment.handleTempInput( line ) ); }
public BiomeTemperatureEnvironment( AbstractConfigField field, String line ) { super( field, line ); }
/** @return The string name of this environment, as it would appear in a config file. */
@Override
public String name() { return EnvironmentListField.ENV_BIOME_TEMPERATURE; }
/** @return The string value of this environment, as it would appear in a config file. */
@Override
public String value() {
if( COMPARATOR == ComparisonOperator.LESS_THAN && VALUE == 0.15F )
return TemperatureEnvironment.FREEZING;
if( COMPARATOR == ComparisonOperator.GREATER_THAN && VALUE == Math.nextDown( 0.15F ) )
return "!" + TemperatureEnvironment.FREEZING;
return super.value();
}
/** @return Returns the actual value to compare, or Float.NaN if there isn't enough information. */
@Override
public float getActual( World world, @Nullable BlockPos pos ) {

View file

@ -12,15 +12,20 @@ import javax.annotation.Nullable;
public class TemperatureEnvironment extends CompareFloatEnvironment {
public static final String FREEZING = "freezing";
public static final float FREEZING_POINT = 0.15F;
public static String handleTempInput( String line ) {
if( line.equalsIgnoreCase( FREEZING ) )
return ComparisonOperator.LESS_THAN + " " + 0.15F;
return ComparisonOperator.LESS_THAN + " " + FREEZING_POINT;
if( line.equalsIgnoreCase( "!" + FREEZING ) )
return ComparisonOperator.GREATER_THAN + " " + Math.nextDown( 0.15F );
return ComparisonOperator.LESS_THAN.invert() + " " + FREEZING_POINT;
return line;
}
public TemperatureEnvironment( boolean freezing ) {
this( ComparisonOperator.LESS_THAN.invert( !freezing ), FREEZING_POINT );
}
public TemperatureEnvironment( ComparisonOperator op, float value ) { super( op, value ); }
public TemperatureEnvironment( AbstractConfigField field, String line ) { super( field, handleTempInput( line ) ); }
@ -32,10 +37,8 @@ public class TemperatureEnvironment extends CompareFloatEnvironment {
/** @return The string value of this environment, as it would appear in a config file. */
@Override
public String value() {
if( COMPARATOR == ComparisonOperator.LESS_THAN && VALUE == 0.15F )
return FREEZING;
if( COMPARATOR == ComparisonOperator.GREATER_THAN && VALUE == Math.nextDown( 0.15F ) )
return "!" + FREEZING;
if( COMPARATOR == ComparisonOperator.LESS_THAN && VALUE == FREEZING_POINT ) return FREEZING;
if( COMPARATOR == ComparisonOperator.LESS_THAN.invert() && VALUE == FREEZING_POINT ) return "!" + FREEZING;
return super.value();
}

View file

@ -35,8 +35,6 @@ public class DimensionPropertyEnvironment extends EnumEnvironment<DimensionPrope
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() ); }

View file

@ -13,8 +13,6 @@ 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 ); }

View file

@ -4,6 +4,7 @@ import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.config.field.EnvironmentListField;
import fathertoast.specialmobs.common.config.util.environment.DynamicRegistryGroupEnvironment;
import net.minecraft.util.RegistryKey;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.DimensionType;
@ -14,9 +15,9 @@ import java.util.List;
public class DimensionTypeGroupEnvironment extends DynamicRegistryGroupEnvironment<DimensionType> {
public DimensionTypeGroupEnvironment( RegistryKey<DimensionType> dimType ) { super( dimType.getRegistryName() ); }
public DimensionTypeGroupEnvironment( RegistryKey<DimensionType> dimType, boolean invert ) { this( dimType.getRegistryName(), invert ); }
public DimensionTypeGroupEnvironment( RegistryKey<DimensionType> dimType, boolean invert ) { super( dimType.getRegistryName(), invert ); }
public DimensionTypeGroupEnvironment( ResourceLocation regKey, boolean invert ) { super( regKey, invert ); }
public DimensionTypeGroupEnvironment( AbstractConfigField field, String line ) { super( field, line ); }

View file

@ -18,7 +18,7 @@ public class PositionEnvironment extends EnumEnvironment<PositionEnvironment.Val
IS_IN_VILLAGE( ( world, pos ) -> pos != null && world instanceof ServerWorld && ((ServerWorld) world).isVillage( pos ) ),
IS_NEAR_VILLAGE( ( world, pos ) -> pos != null && world instanceof ServerWorld &&
((ServerWorld) world).isCloseToVillage( pos, 3 ) ),
IS_RAIDED( ( world, pos ) -> pos != null && world instanceof ServerWorld && ((ServerWorld) world).isRaided( pos ) ),
IS_NEAR_RAID( ( world, pos ) -> pos != null && world instanceof ServerWorld && ((ServerWorld) world).isRaided( pos ) ),
IS_IN_WATER( ( world, pos ) -> pos != null && world.getFluidState( pos ).is( FluidTags.WATER ) ),
IS_IN_LAVA( ( world, pos ) -> pos != null && world.getFluidState( pos ).is( FluidTags.LAVA ) ),
IS_IN_FLUID( ( world, pos ) -> pos != null && !world.getFluidState( pos ).isEmpty() ),
@ -31,8 +31,6 @@ public class PositionEnvironment extends EnumEnvironment<PositionEnvironment.Val
public boolean of( World world, @Nullable BlockPos pos ) { return SUPPLIER.apply( world, pos ); }
}
public PositionEnvironment( Value value ) { super( value ); }
public PositionEnvironment( Value value, boolean invert ) { super( value, invert ); }
public PositionEnvironment( AbstractConfigField field, String line ) { super( field, line, Value.values() ); }

View file

@ -14,8 +14,6 @@ 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 ); }

View file

@ -3,6 +3,7 @@ package fathertoast.specialmobs.common.config.util.environment.position;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.config.field.EnvironmentListField;
import fathertoast.specialmobs.common.config.util.environment.RegistryGroupEnvironment;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.structure.Structure;
@ -16,9 +17,9 @@ import java.util.List;
public class StructureGroupEnvironment extends RegistryGroupEnvironment<Structure<?>> {
public StructureGroupEnvironment( Structure<?> biome ) { super( biome ); }
public StructureGroupEnvironment( Structure<?> structure, boolean invert ) { super( structure, invert ); }
public StructureGroupEnvironment( Structure<?> biome, boolean invert ) { super( biome, invert ); }
public StructureGroupEnvironment( ResourceLocation regKey, boolean invert ) { super( regKey, invert ); }
public StructureGroupEnvironment( AbstractConfigField field, String line ) { super( field, line ); }

View file

@ -0,0 +1,33 @@
package fathertoast.specialmobs.common.config.util.environment.time;
import fathertoast.specialmobs.common.config.field.AbstractConfigField;
import fathertoast.specialmobs.common.config.field.EnvironmentListField;
import fathertoast.specialmobs.common.config.util.environment.CompareLongEnvironment;
import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nullable;
public class ChunkTimeEnvironment extends CompareLongEnvironment {
public ChunkTimeEnvironment( ComparisonOperator op, long value ) { super( op, value ); }
public ChunkTimeEnvironment( AbstractConfigField field, String line ) { super( field, line ); }
/** @return The minimum value that can be given to the value. */
@Override
protected long getMinValue() { return 0L; }
/** @return The string name of this environment, as it would appear in a config file. */
@Override
public String name() { return EnvironmentListField.ENV_CHUNK_TIME; }
/** @return Returns the actual value to compare, or null if there isn't enough information. */
@Override
public Long getActual( World world, @Nullable BlockPos pos ) {
// Ignore deprecation; this is intentionally the same method used by World#getCurrentDifficultyAt
//noinspection deprecation
return pos == null || !world.hasChunkAt( pos ) ? null : world.getChunkAt( pos ).getInhabitedTime();
}
}

View file

@ -27,8 +27,6 @@ public class DayTimeEnvironment extends EnumEnvironment<DayTimeEnvironment.Value
}
}
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() ); }

View file

@ -26,8 +26,11 @@ public class DifficultyEnvironment extends CompareFloatEnvironment {
public DifficultyEnvironment( AbstractConfigField field, String line ) { super( field, line ); }
/** @return The minimum value that can be given to the value. */
@Override
protected float getMinValue() { return 0.0F; }
// Don't specify the max value, just in case a mod changes it.
/** @return The string name of this environment, as it would appear in a config file. */
@Override
public String name() { return EnvironmentListField.ENV_DIFFICULTY; }

View file

@ -17,9 +17,11 @@ public class MoonBrightnessEnvironment extends CompareFloatEnvironment {
public MoonBrightnessEnvironment( AbstractConfigField field, String line ) { super( field, line ); }
/** @return The minimum value that can be given to the value. */
@Override
protected float getMinValue() { return 0.0F; }
/** @return The maximum value that can be given to the value. */
@Override
protected float getMaxValue() { return 1.0F; }
/** @return The string name of this environment, as it would appear in a config file. */

View file

@ -19,8 +19,6 @@ public class MoonPhaseEnvironment extends EnumEnvironment<MoonPhaseEnvironment.V
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() ); }

View file

@ -23,9 +23,11 @@ public class SpecialDifficultyEnvironment extends CompareFloatEnvironment {
public SpecialDifficultyEnvironment( AbstractConfigField field, String line ) { super( field, line ); }
/** @return The minimum value that can be given to the value. */
@Override
protected float getMinValue() { return 0.0F; }
/** @return The maximum value that can be given to the value. */
@Override
protected float getMaxValue() { return 1.0F; }
/** @return The string name of this environment, as it would appear in a config file. */

View file

@ -12,15 +12,13 @@ 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; }
public String name() { return EnvironmentListField.ENV_WEATHER; }
/** @return Returns true if this environment matches the provided environment. */
@Override

View file

@ -21,7 +21,7 @@ public class WorldTimeEnvironment extends CompareLongEnvironment {
/** @return The string name of this environment, as it would appear in a config file. */
@Override
public String name() { return EnvironmentListField.ENV_MOON_BRIGHTNESS; }
public String name() { return EnvironmentListField.ENV_WORLD_TIME; }
/** @return Returns the actual value to compare, or null if there isn't enough information. */
@Override

View file

@ -105,7 +105,7 @@ public final class SpecialMobReplacer {
/** @return True if the next mob should be made a special variant. */
private static boolean shouldMakeNextSpecial( MobFamily<?, ?> mobFamily, World world, BlockPos entityPos ) {
return world.random.nextFloat() < mobFamily.config.GENERAL.specialVariantChance.get(); //TODO environment exceptions
return world.random.nextDouble() < mobFamily.config.GENERAL.specialVariantChance.get( world, entityPos );
}
/** @return True if a mob should be replaced. */