mirror of
https://github.com/FatherToast/SpecialMobs.git
synced 2025-05-29 15:26:38 +00:00
Environment-sensitive special chance!
This commit is contained in:
parent
999d9b3edf
commit
74a106ba6a
38 changed files with 520 additions and 170 deletions
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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 ) );
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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: {}",
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 ) ); }
|
||||
}
|
||||
}
|
|
@ -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<>();
|
||||
|
|
|
@ -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! :(" );
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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; }
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 ); }
|
||||
|
|
|
@ -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 ); }
|
||||
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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() ); }
|
||||
|
|
|
@ -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 ); }
|
||||
|
|
|
@ -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 ); }
|
||||
|
||||
|
|
|
@ -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() ); }
|
||||
|
|
|
@ -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 ); }
|
||||
|
|
|
@ -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 ); }
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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() ); }
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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() ); }
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
Loading…
Add table
Reference in a new issue