From aec6d5555a7747c7ab2378c07312f50655b32364 Mon Sep 17 00:00:00 2001 From: FatherToast Date: Wed, 13 Jul 2022 10:58:27 -0500 Subject: [PATCH 01/16] nothing to see here --- .../specialmobs/common/core/SpecialMobReplacer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobReplacer.java b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobReplacer.java index 0ba3626..359c630 100644 --- a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobReplacer.java +++ b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobReplacer.java @@ -39,7 +39,8 @@ public final class SpecialMobReplacer { */ @SubscribeEvent( priority = EventPriority.LOWEST ) public static void onEntitySpawn( EntityJoinWorldEvent event ) { - if( event.getWorld().isClientSide() || !Config.MAIN.GENERAL.enableMobReplacement.get() ) return; + if( event.getWorld().isClientSide() || !Config.MAIN.GENERAL.enableMobReplacement.get() || event.isCanceled() ) + return; final Entity entity = event.getEntity(); final MobFamily mobFamily = getReplacingMobFamily( entity ); From 7baddc723005c19673ac3900791bf40e90fe90af Mon Sep 17 00:00:00 2001 From: FatherToast Date: Thu, 14 Jul 2022 11:33:17 -0500 Subject: [PATCH 02/16] more lazy impls --- .../common/config/field/EntityListField.java | 24 ++----- .../common/config/util/AttributeEntry.java | 10 ++- .../common/config/util/EntityEntry.java | 66 +++++++++++++------ 3 files changed, 61 insertions(+), 39 deletions(-) diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/EntityListField.java b/src/main/java/fathertoast/specialmobs/common/config/field/EntityListField.java index 1604190..c50b4cb 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/EntityListField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/EntityListField.java @@ -5,10 +5,8 @@ import fathertoast.specialmobs.common.config.util.EntityEntry; import fathertoast.specialmobs.common.config.util.EntityList; import fathertoast.specialmobs.common.core.SpecialMobs; import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.registries.ForgeRegistries; import javax.annotation.Nullable; import java.util.ArrayList; @@ -85,16 +83,12 @@ public class EntityListField extends GenericField { List list = TomlHelper.parseStringList( raw ); List entryList = new ArrayList<>(); for( String line : list ) { - EntityEntry entry = parseEntry( line ); - if( entry != null ) { - entryList.add( entry ); - } + entryList.add( parseEntry( line ) ); } value = new EntityList( entryList ); } - /** Parses a single entry line and returns a valid result if possible, or null if the entry is completely invalid. */ - @Nullable + /** Parses a single entry line and returns the result. */ private EntityEntry parseEntry( final String line ) { String modifiedLine = line; @@ -110,20 +104,14 @@ public class EntityListField extends GenericField { // Parse the entity-value array final String[] args = modifiedLine.split( " " ); - final EntityType entityType; + final ResourceLocation regKey; if( REG_KEY_DEFAULT.equalsIgnoreCase( args[0].trim() ) ) { // Handle the special case of a default entry - entityType = null; + regKey = null; } else { // Normal entry - final ResourceLocation regKey = new ResourceLocation( args[0].trim() ); - if( !ForgeRegistries.ENTITIES.containsKey( regKey ) ) { - SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Deleting entry. Invalid entry: {}", - getClass(), getKey(), line ); - return null; - } - entityType = ForgeRegistries.ENTITIES.getValue( regKey ); + regKey = new ResourceLocation( args[0].trim() ); } final List valuesList = new ArrayList<>(); final int reqValues = valueDefault.getRequiredValues(); @@ -173,7 +161,7 @@ public class EntityListField extends GenericField { for( int i = 0; i < values.length; i++ ) { values[i] = valuesList.get( i ); } - return new EntityEntry( entityType, extendable, values ); + return new EntityEntry( this, regKey, extendable, values ); } /** Parses a single value argument and returns a valid result. */ diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/AttributeEntry.java b/src/main/java/fathertoast/specialmobs/common/config/util/AttributeEntry.java index 651ba2d..5a4f967 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/AttributeEntry.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/AttributeEntry.java @@ -2,6 +2,7 @@ package fathertoast.specialmobs.common.config.util; import fathertoast.specialmobs.common.config.field.AbstractConfigField; import fathertoast.specialmobs.common.core.SpecialMobs; +import mcp.MethodsReturnNonnullByDefault; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.ai.attributes.Attribute; import net.minecraft.entity.ai.attributes.AttributeModifierMap; @@ -10,12 +11,17 @@ import net.minecraft.entity.ai.attributes.ModifiableAttributeInstance; import net.minecraft.util.ResourceLocation; import net.minecraftforge.registries.ForgeRegistries; +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + /** * One attribute-operation-value entry in an attribute list. Uses a 'lazy' implementation so the attribute registry is * not polled until this entry is actually used. *

* See also {@link ConfigDrivenAttributeModifierMap} */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault @SuppressWarnings( "unused" ) public class AttributeEntry { /** The field containing this entry. We save a reference to help improve error/warning reports. */ @@ -48,7 +54,7 @@ public class AttributeEntry { } /** Creates an entry with the specified values. */ - public AttributeEntry( AbstractConfigField field, ResourceLocation regKey, boolean multiply, double value ) { + public AttributeEntry( @Nullable AbstractConfigField field, @Nullable ResourceLocation regKey, boolean multiply, double value ) { FIELD = field; ATTRIBUTE_KEY = regKey; MULTIPLY = multiply; @@ -95,7 +101,7 @@ public class AttributeEntry { } /** Applies this attribute change to the attribute instance. Assumes that the instance is for this entry's target attribute. */ - private void apply( ModifiableAttributeInstance attributeInstance ) { + private void apply( @Nullable ModifiableAttributeInstance attributeInstance ) { if( attributeInstance == null ) throw new IllegalStateException( "Attempted to modify non-registered attribute " + ATTRIBUTE_KEY ); diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/EntityEntry.java b/src/main/java/fathertoast/specialmobs/common/config/util/EntityEntry.java index 7058053..65218e1 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/EntityEntry.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/EntityEntry.java @@ -1,5 +1,6 @@ package fathertoast.specialmobs.common.config.util; +import fathertoast.specialmobs.common.config.field.AbstractConfigField; import fathertoast.specialmobs.common.config.field.EntityListField; import fathertoast.specialmobs.common.core.SpecialMobs; import net.minecraft.entity.Entity; @@ -8,58 +9,84 @@ import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; import net.minecraftforge.registries.ForgeRegistries; +import javax.annotation.Nullable; + /** - * One entity-value entry in an entity list. + * One entity-value entry in an entity list. Uses a 'lazy' implementation so the entity type registry is + * not polled until this entry is actually used. */ @SuppressWarnings( "unused" ) public class EntityEntry { - /** The entity type this entry is defined for. If this is null, then this entry will match any entity. */ - public final EntityType TYPE; + /** The field containing this entry. We save a reference to help improve error/warning reports. */ + private final AbstractConfigField FIELD; + + /** The registry key for this entry's entity type. */ + public final ResourceLocation ENTITY_KEY; /** True if this should check for instanceof the entity class (as opposed to equals). */ public final boolean EXTEND; /** The values given to this entry. Null for comparison objects. */ public final double[] VALUES; + /** The entity type this entry is defined for. If this is null, then this entry will match any entity. */ + private EntityType entityType; /** The class this entry is defined for. This is not assigned until a world has been loaded. */ Class entityClass; /** Creates an entry used to compare entity classes internally with the entries in an entity list. */ EntityEntry( Entity entity ) { - TYPE = entity.getType(); + FIELD = null; + ENTITY_KEY = entity.getType().getRegistryName(); EXTEND = false; VALUES = null; + entityType = entity.getType(); entityClass = entity.getClass(); } /** Creates an entry with the specified values that acts as a default matching all entity types. Used for creating default configs. */ - public EntityEntry( double... values ) { - this( null, true, values ); - } + public EntityEntry( double... values ) { this( null, true, values ); } /** Creates an extendable entry with the specified values. Used for creating default configs. */ - public EntityEntry( EntityType entityType, double... values ) { - this( entityType, true, values ); - } + public EntityEntry( @Nullable EntityType type, double... values ) { this( type, true, values ); } /** Creates an entry with the specified values. Used for creating default configs. */ - public EntityEntry( EntityType entityType, boolean extend, double... values ) { - TYPE = entityType; + public EntityEntry( @Nullable EntityType type, boolean extend, double... values ) { + this( null, type == null ? null : type.getRegistryName(), extend, values ); + entityType = type; + } + + /** Creates an entry with the specified values. */ + public EntityEntry( @Nullable AbstractConfigField field, @Nullable ResourceLocation regKey, boolean extend, double... values ) { + FIELD = field; + ENTITY_KEY = regKey; EXTEND = extend; VALUES = values; } + /** @return Loads the entity type from registry. Returns true if successful. */ + private boolean validate() { + if( entityType != null || ENTITY_KEY == null ) return true; // Null entity key means this is a default entry + + if( !ForgeRegistries.ENTITIES.containsKey( ENTITY_KEY ) ) { + SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Invalid entry: {}", + FIELD.getClass(), FIELD.getKey(), ENTITY_KEY.toString() ); + return false; + } + entityType = ForgeRegistries.ENTITIES.getValue( ENTITY_KEY ); + return true; + } + /** Called on this entry before using it to check if the entity class has been determined, and loads the class if it has not been. */ void checkClass( World world ) { - if( TYPE != null && entityClass == null ) { + if( validate() && entityType != null && entityClass == null ) { try { - final Entity entity = TYPE.create( world ); + final Entity entity = entityType.create( world ); if( entity != null ) { entityClass = entity.getClass(); entity.remove(); } } catch( Exception ex ) { - SpecialMobs.LOG.warn( "Failed to load class of entity type {}!", TYPE ); + SpecialMobs.LOG.warn( "Failed to load class of entity type {}!", entityType ); ex.printStackTrace(); } } @@ -72,9 +99,11 @@ public class EntityEntry { * entries for the same class in a list. */ public boolean contains( EntityEntry entry ) { + if( !validate() ) return false; + // Handle default entries - if( TYPE == null ) return true; - if( entry.TYPE == null ) return false; + if( entityType == null ) return true; + if( entry.entityType == null ) return false; // Same entity, but non-extendable is more specific if( entityClass == entry.entityClass ) return !entry.EXTEND; // Extendable entry, check if the other is for a subclass @@ -91,8 +120,7 @@ public class EntityEntry { @Override public String toString() { // Start with the entity type registry key - ResourceLocation resource = TYPE == null ? null : ForgeRegistries.ENTITIES.getKey( TYPE ); - StringBuilder str = new StringBuilder( resource == null ? EntityListField.REG_KEY_DEFAULT : resource.toString() ); + StringBuilder str = new StringBuilder( ENTITY_KEY == null ? EntityListField.REG_KEY_DEFAULT : ENTITY_KEY.toString() ); // Insert "specific" prefix if not extendable if( !EXTEND ) { str.insert( 0, '~' ); From c16dd2c61ee8c13cd69cac9fda5c642c19e967e2 Mon Sep 17 00:00:00 2001 From: FatherToast Date: Fri, 15 Jul 2022 17:29:35 -0500 Subject: [PATCH 03/16] Fix blazy eye height --- .../common/entity/blaze/_SpecialBlazeEntity.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/fathertoast/specialmobs/common/entity/blaze/_SpecialBlazeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/blaze/_SpecialBlazeEntity.java index 0093809..3f96ecf 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/blaze/_SpecialBlazeEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/blaze/_SpecialBlazeEntity.java @@ -205,11 +205,11 @@ public class _SpecialBlazeEntity extends BlazeEntity implements IRangedAttackMob getSpecialData().tick(); } - /** @return The eye height of this entity when standing. */ - @Override - protected float getStandingEyeHeight( Pose pose, EntitySize size ) { - return super.getStandingEyeHeight( pose, size ) * getSpecialData().getBaseScale() * (isBaby() ? 0.53448F : 1.0F); - } + // /** @return The eye height of this entity when standing. */ - Blazes use auto-scaled eye height + // @Override + // protected float getStandingEyeHeight( Pose pose, EntitySize size ) { + // return super.getStandingEyeHeight( pose, size ) * getSpecialData().getBaseScale() * (isBaby() ? 0.53448F : 1.0F); + // } /** @return Whether this entity is immune to fire damage. */ @Override From ab7002c22ce52f5741e14e54340d0ed5ed1551dd Mon Sep 17 00:00:00 2001 From: FatherToast Date: Sun, 17 Jul 2022 02:01:55 -0500 Subject: [PATCH 04/16] okie I will use these tomorrow --- .../common/bestiary/BestiaryInfo.java | 13 +- .../common/bestiary/MobFamily.java | 6 - .../common/bestiary/package-info.java | 7 + .../common/config/family/package-info.java | 7 + .../config/field/AbstractConfigField.java | 4 +- .../config/field/AttributeListField.java | 19 +- .../common/config/field/BlockListField.java | 13 +- .../common/config/field/BooleanField.java | 2 +- .../common/config/field/DoubleField.java | 4 +- .../common/config/field/EntityListField.java | 52 +++- .../common/config/field/EnumField.java | 5 +- .../config/field/EnvironmentListField.java | 291 ++++++++++++++++++ .../common/config/field/GenericField.java | 4 +- .../common/config/field/IntField.java | 4 +- .../field/LazyRegistryEntryListField.java | 2 +- .../config/field/RegistryEntryListField.java | 13 +- .../config/field/ScaledDoubleField.java | 4 +- .../common/config/field/SqrDoubleField.java | 4 +- .../common/config/field/package-info.java | 7 + .../common/config/file/TomlHelper.java | 33 +- .../common/config/file/package-info.java | 7 + .../common/config/species/package-info.java | 7 + .../common/config/util/AttributeList.java | 3 +- .../common/config/util/BlockEntry.java | 18 +- .../common/config/util/BlockList.java | 14 +- .../common/config/util/ConfigUtil.java | 18 +- .../common/config/util/EntityList.java | 14 +- .../common/config/util/EnvironmentEntry.java | 112 +++++++ .../common/config/util/EnvironmentList.java | 100 ++++++ .../common/config/util/RegistryEntryList.java | 13 +- .../common/config/util/WeightedList.java | 2 + .../util/environment/AbstractEnvironment.java | 21 ++ .../environment/CompareFloatEnvironment.java | 87 ++++++ .../environment/CompareIntEnvironment.java | 87 ++++++ .../environment/CompareLongEnvironment.java | 87 ++++++ .../util/environment/ComparisonOperator.java | 78 +++++ .../DynamicRegistryEnvironment.java | 78 +++++ .../DynamicRegistryGroupEnvironment.java | 87 ++++++ .../util/environment/EnumEnvironment.java | 41 +++ .../util/environment/RegistryEnvironment.java | 59 ++++ .../environment/RegistryGroupEnvironment.java | 67 ++++ .../biome/BiomeCategoryEnvironment.java | 52 ++++ .../environment/biome/BiomeEnvironment.java | 36 +++ .../biome/BiomeGroupEnvironment.java | 43 +++ .../biome/BiomeTemperatureEnvironment.java | 37 +++ .../biome/RainfallEnvironment.java | 37 +++ .../biome/TemperatureEnvironment.java | 47 +++ .../util/environment/biome/package-info.java | 7 + .../DimensionPropertyEnvironment.java | 51 +++ .../dimension/DimensionTypeEnvironment.java | 36 +++ .../DimensionTypeGroupEnvironment.java | 41 +++ .../environment/dimension/package-info.java | 7 + .../config/util/environment/package-info.java | 7 + .../position/StructureEnvironment.java | 38 +++ .../position/StructureGroupEnvironment.java | 46 +++ .../environment/position/YEnvironment.java | 25 ++ .../position/YFromSeaEnvironment.java | 25 ++ .../environment/position/package-info.java | 7 + .../environment/time/DayTimeEnvironment.java | 45 +++ .../time/DifficultyEnvironment.java | 40 +++ .../time/MoonBrightnessEnvironment.java | 34 ++ .../time/MoonPhaseEnvironment.java | 38 +++ .../time/SpecialDifficultyEnvironment.java | 40 +++ .../time/TimeFromMidnightEnvironment.java | 37 +++ .../environment/time/WeatherEnvironment.java | 32 ++ .../time/WorldTimeEnvironment.java | 29 ++ .../util/environment/time/package-info.java | 7 + .../common/config/util/package-info.java | 7 + .../common/core/SpecialMobReplacer.java | 4 - .../specialmobs/common/core/package-info.java | 7 + .../common/core/register/package-info.java | 7 + 71 files changed, 2256 insertions(+), 107 deletions(-) create mode 100644 src/main/java/fathertoast/specialmobs/common/bestiary/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/family/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/field/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/file/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/species/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentList.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/AbstractEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/CompareFloatEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/CompareIntEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/CompareLongEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/ComparisonOperator.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryGroupEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/EnumEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryGroupEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategoryEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeGroupEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeTemperatureEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/RainfallEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TemperatureEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionPropertyEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeGroupEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureGroupEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/position/YEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/position/YFromSeaEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/position/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DayTimeEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DifficultyEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/time/MoonBrightnessEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/time/MoonPhaseEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/time/SpecialDifficultyEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/time/TimeFromMidnightEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/time/WeatherEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/time/WorldTimeEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/time/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/core/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/core/register/package-info.java diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java b/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java index da1ea1c..bbd0270 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java @@ -13,6 +13,7 @@ import net.minecraft.potion.Effects; import net.minecraft.util.ResourceLocation; import net.minecraftforge.registries.ForgeRegistries; +import javax.annotation.Nullable; import java.util.*; /** @@ -280,13 +281,13 @@ public class BestiaryInfo { public Builder vanillaTextureBaseOnly( String tex ) { return vanillaBaseTexture( tex ).noEyesTexture().noOverlayTexture(); } /** Sets the species default base texture. */ - private Builder vanillaBaseTexture( String tex ) { return baseTexture( tex == null ? null : new ResourceLocation( tex ) ); } + private Builder vanillaBaseTexture( String tex ) { return baseTexture( new ResourceLocation( tex ) ); } /** Sets the species default glowing eyes texture. */ - private Builder vanillaEyesTexture( String eyeTex ) { return eyesTexture( eyeTex == null ? null : new ResourceLocation( eyeTex ) ); } + private Builder vanillaEyesTexture( String eyeTex ) { return eyesTexture( new ResourceLocation( eyeTex ) ); } /** Sets the species default overlay texture. */ - private Builder vanillaOverlayTexture( String ovrTex ) { return overlayTexture( ovrTex == null ? null : new ResourceLocation( ovrTex ) ); } + private Builder vanillaOverlayTexture( String ovrTex ) { return overlayTexture( new ResourceLocation( ovrTex ) ); } //--------------- Textures (Auto-selected) ---------------- @@ -351,19 +352,19 @@ public class BestiaryInfo { public Builder noAnimationTexture() { return noOverlayTexture(); } /** Sets the species default base texture. */ - private Builder baseTexture( ResourceLocation tex ) { + private Builder baseTexture( @Nullable ResourceLocation tex ) { texture = tex; return this; } /** Sets the species default glowing eyes texture. */ - private Builder eyesTexture( ResourceLocation eyeTex ) { + private Builder eyesTexture( @Nullable ResourceLocation eyeTex ) { eyesTexture = eyeTex; return this; } /** Sets the species default overlay texture. */ - private Builder overlayTexture( ResourceLocation ovrTex ) { + private Builder overlayTexture( @Nullable ResourceLocation ovrTex ) { overlayTexture = ovrTex; return this; } diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java index c19b792..affbf32 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java @@ -6,7 +6,6 @@ import fathertoast.specialmobs.common.core.register.SMEntities; import fathertoast.specialmobs.common.core.register.SMItems; import fathertoast.specialmobs.common.util.AnnotationHelper; import fathertoast.specialmobs.common.util.References; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; @@ -18,7 +17,6 @@ import net.minecraftforge.common.ForgeSpawnEggItem; import net.minecraftforge.fml.RegistryObject; import javax.annotation.Nullable; -import javax.annotation.ParametersAreNonnullByDefault; import java.util.*; import java.util.function.Function; @@ -28,8 +26,6 @@ import java.util.function.Function; * * @see MobFamily.Species */ -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault public class MobFamily { /** List of all families, generated to make iteration possible. */ private static final ArrayList> FAMILY_LIST = new ArrayList<>(); @@ -238,8 +234,6 @@ public class MobFamily { * * @see MobFamily */ - @ParametersAreNonnullByDefault - @MethodsReturnNonnullByDefault public static class Species { /** The special mob family this species belongs to. */ public final MobFamily family; diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/package-info.java b/src/main/java/fathertoast/specialmobs/common/bestiary/package-info.java new file mode 100644 index 0000000..52dc0a1 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.bestiary; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/family/package-info.java b/src/main/java/fathertoast/specialmobs/common/config/family/package-info.java new file mode 100644 index 0000000..5ff563b --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/family/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.config.family; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/AbstractConfigField.java b/src/main/java/fathertoast/specialmobs/common/config/field/AbstractConfigField.java index a072b2d..7120589 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/AbstractConfigField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/AbstractConfigField.java @@ -25,7 +25,7 @@ public abstract class AbstractConfigField { * Creates a new field with the supplied key and description. * If the description is null, it will cancel the entire comment, including the automatic field info text. */ - protected AbstractConfigField( String key, String... description ) { + protected AbstractConfigField( String key, @Nullable String... description ) { this( loadingCategory + key, description == null ? null : TomlHelper.newComment( description ) ); } @@ -33,7 +33,7 @@ public abstract class AbstractConfigField { * Creates a new field with the supplied key and comment. This method is only used for very special circumstances. * If the comment is null, it will cancel the entire comment, including the automatic field info text. */ - AbstractConfigField( String key, List comment ) { + AbstractConfigField( String key, @Nullable List comment ) { KEY = key; COMMENT = comment; } diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/AttributeListField.java b/src/main/java/fathertoast/specialmobs/common/config/field/AttributeListField.java index 49a0df4..4a7efe4 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/AttributeListField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/AttributeListField.java @@ -42,16 +42,10 @@ public class AttributeListField extends GenericField { public ConfigDrivenAttributeModifierMap linkedAttributeMap; /** Creates a new field. */ - public AttributeListField( String key, AttributeList defaultValue, String... description ) { + public AttributeListField( String key, AttributeList defaultValue, @Nullable String... description ) { super( key, defaultValue, description ); } - /** Applies all attribute changes in this list to the entity attribute builder. */ - public void apply( AttributeModifierMap.MutableAttribute builder ) { get().apply( builder ); } - - /** Applies all attribute changes in this list to the entity. */ - public void apply( LivingEntity entity ) { get().apply( entity ); } - /** Adds info about the field type, format, and bounds to the end of a field's description. */ public void appendFieldInfo( List comment ) { comment.add( TomlHelper.fieldInfoFormat( "Attribute List", valueDefault, @@ -81,7 +75,7 @@ public class AttributeListField extends GenericField { if( linkedAttributeMap != null ) linkedAttributeMap.invalidate(); } - /** Parses a single entry line and returns a valid result if possible, or null if the entry is completely invalid. */ + /** Parses a single entry line and returns a valid result. */ private AttributeEntry parseEntry( final String line ) { // Parse the attribute-operation-value array final String[] args = line.split( " ", 4 ); @@ -148,4 +142,13 @@ public class AttributeListField extends GenericField { } return value; } + + + // Convenience methods + + /** Applies all attribute changes in this list to the entity attribute builder. */ + public void apply( AttributeModifierMap.MutableAttribute builder ) { get().apply( builder ); } + + /** Applies all attribute changes in this list to the entity. */ + public void apply( LivingEntity entity ) { get().apply( entity ); } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/BlockListField.java b/src/main/java/fathertoast/specialmobs/common/config/field/BlockListField.java index fc0ae85..04f0712 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/BlockListField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/BlockListField.java @@ -28,7 +28,7 @@ public class BlockListField extends GenericField { } /** Creates a new field. */ - public BlockListField( String key, BlockList defaultValue, String... description ) { + public BlockListField( String key, BlockList defaultValue, @Nullable String... description ) { super( key, defaultValue, description ); } @@ -53,6 +53,15 @@ public class BlockListField extends GenericField { value = new BlockList( this, TomlHelper.parseStringList( raw ) ); } + + // Convenience methods + + /** @return Returns true if there are no entries in this block list. */ + public boolean isEmpty() { return get().isEmpty(); } + + /** @return Returns true if the block is contained in this list. */ + public boolean matches( BlockState blockState ) { return get().matches( blockState ); } + /** * Represents two block list fields, a blacklist and a whitelist, combined into one. */ @@ -73,7 +82,7 @@ public class BlockListField extends GenericField { /** @return Returns true if the block is contained in this list. */ public boolean matches( BlockState blockState ) { - return blockState != null && !BLACKLIST.get().matches( blockState ) && WHITELIST.get().matches( blockState ); + return !BLACKLIST.get().matches( blockState ) && WHITELIST.get().matches( blockState ); } } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/BooleanField.java b/src/main/java/fathertoast/specialmobs/common/config/field/BooleanField.java index 6f95a0e..8266444 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/BooleanField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/BooleanField.java @@ -18,7 +18,7 @@ public class BooleanField extends AbstractConfigField { private boolean value; /** Creates a new field. */ - public BooleanField( String key, boolean defaultValue, String... description ) { + public BooleanField( String key, boolean defaultValue, @Nullable String... description ) { super( key, description ); valueDefault = defaultValue; } diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/DoubleField.java b/src/main/java/fathertoast/specialmobs/common/config/field/DoubleField.java index ccfb252..a3ecccd 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/DoubleField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/DoubleField.java @@ -23,12 +23,12 @@ public class DoubleField extends AbstractConfigField { private double value; /** Creates a new field that accepts a common range of values. */ - public DoubleField( String key, double defaultValue, Range range, String... description ) { + public DoubleField( String key, double defaultValue, Range range, @Nullable String... description ) { this( key, defaultValue, range.MIN, range.MAX, description ); } /** Creates a new field that accepts a specialized range of values. */ - public DoubleField( String key, double defaultValue, double min, double max, String... description ) { + public DoubleField( String key, double defaultValue, double min, double max, @Nullable String... description ) { super( key, description ); valueDefault = defaultValue; valueMin = min; diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/EntityListField.java b/src/main/java/fathertoast/specialmobs/common/config/field/EntityListField.java index c50b4cb..ffc7a9c 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/EntityListField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/EntityListField.java @@ -33,7 +33,7 @@ public class EntityListField extends GenericField { } /** Creates a new field. */ - public EntityListField( String key, EntityList defaultValue, String... description ) { + public EntityListField( String key, EntityList defaultValue, @Nullable String... description ) { super( key, defaultValue, description ); } @@ -191,6 +191,36 @@ public class EntityListField extends GenericField { return value; } + + // Convenience methods + + /** @return True if the entity is contained in this list. */ + public boolean contains( @Nullable Entity entity ) { return get().contains( entity ); } + + /** + * @param entity The entity to retrieve values for. + * @return The array of values of the best-match entry. Returns null if the entity is not contained in this entity list. + */ + @Nullable + public double[] getValues( @Nullable Entity entity ) { return get().getValues( entity ); } + + /** + * @param entity The entity to retrieve a value for. + * @return The first value in the best-match entry's value array. Returns 0 if the entity is not contained in this + * entity list or has no values specified. This should only be used for 'single value' lists. + * @see EntityList#setSingleValue() + * @see EntityList#setSinglePercent() + */ + public double getValue( @Nullable Entity entity ) { return get().getValue( entity ); } + + /** + * @param entity The entity to roll a value for. + * @return Randomly rolls the first percentage value in the best-match entry's value array. Returns false if the entity + * is not contained in this entity list or has no values specified. This should only be used for 'single percent' lists. + * @see EntityList#setSinglePercent() + */ + public boolean rollChance( @Nullable LivingEntity entity ) { return get().rollChance( entity ); } + /** * Represents two entity list fields, a blacklist and a whitelist, combined into one. * The blacklist cannot contain values, but the whitelist can have any settings. @@ -210,17 +240,21 @@ public class EntityListField extends GenericField { } } + + // Convenience methods + /** @return True if the entity is contained in this list. */ - public boolean contains( Entity entity ) { - return entity != null && !BLACKLIST.get().contains( entity ) && WHITELIST.get().contains( entity ); + public boolean contains( @Nullable Entity entity ) { + return entity != null && !BLACKLIST.contains( entity ) && WHITELIST.contains( entity ); } /** * @param entity The entity to retrieve values for. * @return The array of values of the best-match entry. Returns null if the entity is not contained in this entity list. */ - public double[] getValues( Entity entity ) { - return entity != null && !BLACKLIST.get().contains( entity ) ? WHITELIST.get().getValues( entity ) : null; + @Nullable + public double[] getValues( @Nullable Entity entity ) { + return entity != null && !BLACKLIST.contains( entity ) ? WHITELIST.getValues( entity ) : null; } /** @@ -230,8 +264,8 @@ public class EntityListField extends GenericField { * @see EntityList#setSingleValue() * @see EntityList#setSinglePercent() */ - public double getValue( Entity entity ) { - return entity != null && !BLACKLIST.get().contains( entity ) ? WHITELIST.get().getValue( entity ) : 0.0; + public double getValue( @Nullable Entity entity ) { + return entity != null && !BLACKLIST.contains( entity ) ? WHITELIST.getValue( entity ) : 0.0; } /** @@ -240,8 +274,8 @@ public class EntityListField extends GenericField { * is not contained in this entity list or has no values specified. This should only be used for 'single percent' lists. * @see EntityList#setSinglePercent() */ - public boolean rollChance( LivingEntity entity ) { - return entity != null && !BLACKLIST.get().contains( entity ) && WHITELIST.get().rollChance( entity ); + public boolean rollChance( @Nullable LivingEntity entity ) { + return entity != null && !BLACKLIST.contains( entity ) && WHITELIST.rollChance( entity ); } } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/EnumField.java b/src/main/java/fathertoast/specialmobs/common/config/field/EnumField.java index ebfa4e2..da4b41c 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/EnumField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/EnumField.java @@ -14,13 +14,13 @@ public class EnumField> extends GenericField { private final T[] valuesValid; /** Creates a new field that accepts any enum value. */ - public EnumField( String key, T defaultValue, String... description ) { + public EnumField( String key, T defaultValue, @Nullable String... description ) { //noinspection unchecked this( key, defaultValue, (T[]) defaultValue.getClass().getEnumConstants(), description ); } /** Creates a new field that accepts the specified set of enum values. */ - public EnumField( String key, T defaultValue, T[] validValues, String... description ) { + public EnumField( String key, T defaultValue, T[] validValues, @Nullable String... description ) { super( key, defaultValue, description ); valuesValid = validValues; } @@ -55,6 +55,7 @@ public class EnumField> extends GenericField { } /** @return Attempts to parse the string literal as one of the valid values for this field and returns it, or null if invalid. */ + @Nullable private T parseValue( String name ) { for( T val : valuesValid ) { if( val.name().equalsIgnoreCase( name ) ) return val; diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java b/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java new file mode 100644 index 0000000..4d4bde8 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java @@ -0,0 +1,291 @@ +package fathertoast.specialmobs.common.config.field; + +import fathertoast.specialmobs.common.config.file.TomlHelper; +import fathertoast.specialmobs.common.config.util.EnvironmentEntry; +import fathertoast.specialmobs.common.config.util.EnvironmentList; +import fathertoast.specialmobs.common.config.util.environment.*; +import fathertoast.specialmobs.common.config.util.environment.biome.*; +import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionPropertyEnvironment; +import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionTypeEnvironment; +import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionTypeGroupEnvironment; +import fathertoast.specialmobs.common.config.util.environment.position.StructureEnvironment; +import fathertoast.specialmobs.common.config.util.environment.position.StructureGroupEnvironment; +import fathertoast.specialmobs.common.config.util.environment.position.YEnvironment; +import fathertoast.specialmobs.common.config.util.environment.position.YFromSeaEnvironment; +import fathertoast.specialmobs.common.config.util.environment.time.*; +import fathertoast.specialmobs.common.core.SpecialMobs; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a config field with an environment list value. + */ +public class EnvironmentListField extends GenericField { + + /* When adding new environment conditions, you must: + * - Add a new constant for its name here + * - Create the environment class and ensure #name() returns the new name constant + * - Link the name to construction in #parseCondition(String,String) below + * - Add any applicable builder methods in EnvironmentEntry.Builder + * - Describe the new environment condition's usage/format in #environmentDescriptions() below + */ + // Dimension-based + public static final String ENV_DIMENSION_PROPERTY = "dimension_property"; + public static final String ENV_DIMENSION_TYPE = "dimension_type"; + // Biome-based + public static final String ENV_RAINFALL = "rainfall"; + public static final String ENV_BIOME_TEMPERATURE = "biome_temp"; + public static final String ENV_TEMPERATURE = "temp"; + public static final String ENV_BIOME_CATEGORY = "biome_category"; + public static final String ENV_BIOME = "biome"; + // Position-based TODO can_see_sky + public static final String ENV_STRUCTURE = "structure"; + public static final String ENV_Y = "y"; + public static final String ENV_Y_FROM_SEA = "y_from_sea"; + // Time-based + public static final String ENV_DIFFICULTY = "difficulty"; + public static final String ENV_SPECIAL_DIFFICULTY = "special_difficulty"; + public static final String ENV_WEATHER = "weather"; + public static final String ENV_MOON_BRIGHTNESS = "moon_brightness"; + public static final String ENV_MOON_PHASE = "moon_phase"; + public static final String ENV_DAY_TIME = "day_time"; + public static final String ENV_TIME_FROM_MIDNIGHT = "time_from_midnight"; + public static final String ENV_WORLD_TIME = "world_time"; + + /** + * Provides a description of how to use environment lists. Recommended to put at the top of any file using environment lists. + * Always use put the environment condition descriptions at the bottom of the file if this is used! + */ + public static List verboseDescription() { + List comment = new ArrayList<>(); + comment.add( "Environment List fields: General format = [ \"value environment1 condition1 & environment2 condition2 & ...\", ... ]" ); + comment.add( " Environment lists are arrays of environment entries. Each entry is a value followed by the environment conditions that must be" ); + comment.add( " satisfied for the value to be chosen. The environments are tested in the order listed, and the first matching entry is chosen." ); + comment.add( " See the bottom of this file for an explanation on each environment condition available." ); + return comment; + } + + /** Provides a detailed description of how to use each environment condition. Recommended to put at the bottom of any file using environment lists. */ + public static List environmentDescriptions() { + List comment = new ArrayList<>(); + comment.add( "Environment conditions (for Environment List entries):" ); + comment.add( " Many environment conditions can be inverted by using \"!\"; these are shown with (!) in the appropriate location." ); + comment.add( " Other environment conditions are numerical comparisons; these use the operators (shown as op) <, >, =, <=, >=, or != to compare value." ); + comment.add( "Valid environment conditions are:" ); + // Dimension-based + comment.add( " \"" + ENV_DIMENSION_PROPERTY + " (!)property\":" ); + comment.add( " Valid property values: " + TomlHelper.literalList( (Object[]) DimensionPropertyEnvironment.Value.values() ) ); + comment.add( " Dimension properties are the true/false values available to dimension types in data packs." ); + comment.add( " See the wiki for more info: [https://minecraft.fandom.com/wiki/Custom_dimension#Syntax]." ); + comment.add( " \"" + ENV_DIMENSION_TYPE + " (!)namespace:dimension_type_name\":" ); + comment.add( " The world's dimension type. In vanilla, these are only \"minecraft:overworld\", \"minecraft:the_nether\", or \"minecraft:the_end\"." ); + // Biome-based + comment.add( " \"" + ENV_RAINFALL + " op value\":" ); + comment.add( " Biome's rainfall parameter. If this is \"= 0\", it checks that rain is disabled. For reference, rainfall > 0.85 suppresses fire." ); + comment.add( " \"" + ENV_BIOME_TEMPERATURE + " op value\" or \"" + ENV_BIOME_TEMPERATURE + " (!)" + TemperatureEnvironment.FREEZING + "\":" ); + comment.add( " Biome's temperature parameter. For reference, freezing is < 0.15 and hot is generally considered > 0.95." ); + comment.add( " \"" + ENV_TEMPERATURE + " op value\" or \"" + ENV_TEMPERATURE + " (!)" + TemperatureEnvironment.FREEZING + "\":" ); + comment.add( " Height-adjusted temperature. For reference, freezing is < 0.15 and hot is generally considered > 0.95." ); + comment.add( " \"" + ENV_BIOME_CATEGORY + " (!)category\":" ); + comment.add( " Valid category values: " + TomlHelper.literalList( (Object[]) BiomeCategoryEnvironment.Value.values() ) ); + comment.add( " \"" + ENV_BIOME + " (!)namespace:biome_name\":" ); + comment.add( " The biome. See the wiki for vanilla biome names (resource locations) [https://minecraft.fandom.com/wiki/Biome#Biome_IDs]." ); + // Position-based + comment.add( " \"" + ENV_STRUCTURE + " (!)namespace:structure_name\":" ); + comment.add( " The structure. See the wiki for vanilla structure names [https://minecraft.fandom.com/wiki/Generated_structures#Locating]." ); + comment.add( " \"" + ENV_Y + " op value\":" ); + comment.add( " The y-value. For reference, sea level is normally 63 and lava level is normally 10." );//TODO change lava level to -54 for MC 1.18 + comment.add( " \"" + ENV_Y_FROM_SEA + " op value\":" ); + comment.add( " The y-value from sea level. Expect the only air <= 0 to be in caves (which may still have direct view of the sky)." ); + // Time-based + comment.add( " \"" + ENV_DIFFICULTY + " op value\":" ); + comment.add( " The regional difficulty (0 to 6.75). This is based on many factors such as difficulty setting, moon brightness, chunk inhabited time, and world time." ); + comment.add( " For reference, this scales up to the max after 63 days in the world and 150 days in a particular chunk, and peaks during full moons." ); + comment.add( " On Peaceful this is always 0, on Easy this is 0.75 to 1.5, on Normal this is 1.5 to 4.0, and on Hard this is 2.25 to 6.75." ); + comment.add( " \"" + ENV_SPECIAL_DIFFICULTY + " op value\":" ); + comment.add( " The 'special multiplier' for regional difficulty (0 to 1). For reference, this is 0 when difficulty <= 2 and 1 when difficulty >= 4." ); + comment.add( " This is always 0 in Easy and below. In Normal, it maxes at absolute peak regional difficulty. In Hard, it starts at 0.125 and maxes out in ~50 days." ); + comment.add( " \"" + ENV_WEATHER + " (!)type\":" ); + comment.add( " Valid type values: " + TomlHelper.literalList( (Object[]) WeatherEnvironment.Value.values() ) ); + comment.add( " \"" + ENV_MOON_BRIGHTNESS + " op value\":" ); + comment.add( " The moon brightness (0 to 1). New moon has 0 brightness, full moon has 1 brightness. Intermediate phases are 0.25, 0.5, or 0.75." ); + comment.add( " \"" + ENV_MOON_PHASE + " (!)phase\":" ); + comment.add( " Valid phase values: " + TomlHelper.literalList( (Object[]) MoonPhaseEnvironment.Value.values() ) ); + comment.add( " \"" + ENV_DAY_TIME + " (!)time\":" ); + comment.add( " Valid time values: " + TomlHelper.literalList( (Object[]) DayTimeEnvironment.Value.values() ) ); + comment.add( " Note that the transition periods, sunset & sunrise, are considered as part of day & night, respectively." ); + comment.add( " \"" + ENV_TIME_FROM_MIDNIGHT + " op value\":" ); + comment.add( " The absolute time in ticks away from midnight. Value must be 0 to 12000." ); + comment.add( " \"" + ENV_MOON_PHASE + " (!)phase\":" ); + comment.add( " Valid phase values: " + TomlHelper.literalList( (Object[]) MoonPhaseEnvironment.Value.values() ) ); + comment.add( " \"" + ENV_WORLD_TIME + " op value\":" ); + comment.add( " The total time the world has existed, in ticks. For reference, each day cycle is 24000 ticks and each lunar cycle is 192000 ticks." ); + return comment; + } + + /** Creates a new field. */ + public EnvironmentListField( String key, EnvironmentList defaultValue, String... description ) { + super( key, defaultValue, description ); + } + + /** Adds info about the field type, format, and bounds to the end of a field's description. */ + public void appendFieldInfo( List comment ) { + comment.add( TomlHelper.fieldInfoFormat( "Environment List", valueDefault, "[ \"value condition1 state1 & condition2 state2 & ...\", ... ]" ) ); + comment.add( " Range for Values: " + TomlHelper.fieldRange( valueDefault.getMinValue(), valueDefault.getMaxValue() ) ); + } + + /** + * Loads this field's value from the given raw toml value. If anything goes wrong, correct it at the lowest level possible. + *

+ * For example, a missing value should be set to the default, while an out-of-range value should be adjusted to the + * nearest in-range value + */ + @Override + public void load( @Nullable Object raw ) { + if( raw == null ) { + value = valueDefault; + return; + } + List list = TomlHelper.parseStringList( raw ); + List entryList = new ArrayList<>(); + for( String line : list ) { + entryList.add( parseEntry( line ) ); + } + value = new EnvironmentList( entryList ); + } + + /** Parses a single entry line and returns the result. */ + private EnvironmentEntry parseEntry( final String line ) { + // Parse the value out of the conditions + final String[] args = line.split( " ", 2 ); + final double value = parseValue( args[0], line ); + + final List conditions = new ArrayList<>(); + if( args.length > 1 ) { + final String[] condArgs = args[1].trim().split( "&" ); + for( String condArg : condArgs ) { + conditions.add( parseCondition( condArg.trim(), line ) ); + } + } + if( conditions.isEmpty() ) { + SpecialMobs.LOG.warn( "No environments defined in entry for {} \"{}\"! Invalid entry: {}", + getClass(), getKey(), line ); + } + + return new EnvironmentEntry( value, conditions ); + } + + /** Parses a single value argument and returns a valid result. */ + private double parseValue( final String arg, final String line ) { + // Try to parse the value + double value; + try { + value = Double.parseDouble( arg ); + } + catch( NumberFormatException ex ) { + // This is thrown if the string is not a parsable number + SpecialMobs.LOG.warn( "Invalid value for {} \"{}\"! Falling back to 0. Invalid entry: {}", + getClass(), getKey(), line ); + value = 0.0; + } + // Verify value is within range + if( value < valueDefault.getMinValue() ) { + SpecialMobs.LOG.warn( "Value for {} \"{}\" is below the minimum ({})! Clamping value. Invalid value: {}", + getClass(), getKey(), valueDefault.getMinValue(), value ); + value = valueDefault.getMinValue(); + } + else if( value > valueDefault.getMaxValue() ) { + SpecialMobs.LOG.warn( "Value for {} \"{}\" is above the maximum ({})! Clamping value. Invalid value: {}", + getClass(), getKey(), valueDefault.getMaxValue(), value ); + value = valueDefault.getMaxValue(); + } + return value; + } + + /** Parses a single environment condition argument and returns a valid result. */ + private AbstractEnvironment parseCondition( final String arg, final String line ) { + // First parse the environment name, since it defines the format for the rest + final String[] args = arg.split( " ", 2 ); + + final String value; + if( args.length < 2 ) value = ""; + else value = args[1].trim(); + + switch( args[0] ) { + // Dimension-based + case ENV_DIMENSION_PROPERTY: + return new DimensionPropertyEnvironment( this, value ); + case ENV_DIMENSION_TYPE: + return value.endsWith( "*" ) ? new DimensionTypeGroupEnvironment( this, value ) : new DimensionTypeEnvironment( this, value ); + // Biome-based + case ENV_RAINFALL: + return new RainfallEnvironment( this, value ); + case ENV_BIOME_TEMPERATURE: + return new BiomeTemperatureEnvironment( this, value ); + case ENV_TEMPERATURE: + return new TemperatureEnvironment( this, value ); + case ENV_BIOME_CATEGORY: + return new BiomeCategoryEnvironment( this, value ); + case ENV_BIOME: + return value.endsWith( "*" ) ? new BiomeGroupEnvironment( this, value ) : new BiomeEnvironment( this, value ); + // Position-based + case ENV_STRUCTURE: + return value.endsWith( "*" ) ? new StructureGroupEnvironment( this, value ) : new StructureEnvironment( this, value ); + case ENV_Y: + return new YEnvironment( this, value ); + case ENV_Y_FROM_SEA: + return new YFromSeaEnvironment( this, value ); + // Time-based + case ENV_DIFFICULTY: + return new DifficultyEnvironment( this, value ); + case ENV_SPECIAL_DIFFICULTY: + return new SpecialDifficultyEnvironment( this, value ); + case ENV_WEATHER: + return new WeatherEnvironment( this, value ); + case ENV_MOON_BRIGHTNESS: + return new MoonBrightnessEnvironment( this, value ); + case ENV_MOON_PHASE: + return new MoonPhaseEnvironment( this, value ); + case ENV_DAY_TIME: + return new DayTimeEnvironment( this, value ); + case ENV_TIME_FROM_MIDNIGHT: + return new TimeFromMidnightEnvironment( this, value ); + case ENV_WORLD_TIME: + return new WorldTimeEnvironment( this, value ); + } + + // The environment name was not recognized; try to provide some good feedback because this field is complicated + final String[] environmentNames = { + // Dimension-based + ENV_DIMENSION_PROPERTY, ENV_DIMENSION_TYPE, + // Biome-based + ENV_RAINFALL, ENV_BIOME_TEMPERATURE, ENV_TEMPERATURE, ENV_BIOME_CATEGORY, ENV_BIOME, + // Position-based + ENV_STRUCTURE, ENV_Y, ENV_Y_FROM_SEA, + // Time-based + ENV_DIFFICULTY, ENV_SPECIAL_DIFFICULTY, ENV_WEATHER, ENV_MOON_BRIGHTNESS, ENV_MOON_PHASE, ENV_DAY_TIME, + ENV_TIME_FROM_MIDNIGHT, ENV_WORLD_TIME + }; + final AbstractEnvironment fallback = new YEnvironment( ComparisonOperator.LESS_THAN, 0 ); + SpecialMobs.LOG.warn( "Invalid environment '{}' for {} \"{}\"! Falling back to \"{}\". Environment name must be in the set [ {} ]. Invalid environment: {}", + args[0], getClass(), getKey(), fallback, TomlHelper.literalList( (Object[]) environmentNames ), line ); + return fallback; + } + + + // Convenience methods + + /** @return The value matching the given environment, or the default value if no matching environment is defined. */ + public double getOrElse( World world, @Nullable BlockPos pos, DoubleField defaultValue ) { return get().getOrElse( world, pos, defaultValue ); } + + /** @return The value matching the given environment, or the default value if no matching environment is defined. */ + public double getOrElse( World world, @Nullable BlockPos pos, double defaultValue ) { return get().getOrElse( world, pos, defaultValue ); } + + /** @return The value matching the given environment, or null if no matching environment is defined. */ + @Nullable + public Double get( World world, @Nullable BlockPos pos ) { return get().get( world, pos ); } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/GenericField.java b/src/main/java/fathertoast/specialmobs/common/config/field/GenericField.java index bc4305e..d9e4094 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/GenericField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/GenericField.java @@ -1,5 +1,7 @@ package fathertoast.specialmobs.common.config.field; +import javax.annotation.Nullable; + /** * Represents a config field with an object value. *

@@ -16,7 +18,7 @@ public abstract class GenericField extends AbstractConfigField { protected T value; /** Creates a new field. */ - public GenericField( String key, T defaultValue, String... description ) { + public GenericField( String key, T defaultValue, @Nullable String... description ) { super( key, description ); valueDefault = defaultValue; } diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/IntField.java b/src/main/java/fathertoast/specialmobs/common/config/field/IntField.java index 8a1bb78..260f0e3 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/IntField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/IntField.java @@ -23,12 +23,12 @@ public class IntField extends AbstractConfigField { private int value; /** Creates a new field that accepts a common range of values. */ - public IntField( String key, int defaultValue, Range range, String... description ) { + public IntField( String key, int defaultValue, Range range, @Nullable String... description ) { this( key, defaultValue, range.MIN, range.MAX, description ); } /** Creates a new field that accepts a specialized range of values. */ - public IntField( String key, int defaultValue, int min, int max, String... description ) { + public IntField( String key, int defaultValue, int min, int max, @Nullable String... description ) { super( key, description ); valueDefault = defaultValue; valueMin = min; diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/LazyRegistryEntryListField.java b/src/main/java/fathertoast/specialmobs/common/config/field/LazyRegistryEntryListField.java index 0a1c882..24f5b58 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/LazyRegistryEntryListField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/LazyRegistryEntryListField.java @@ -17,7 +17,7 @@ import javax.annotation.Nullable; public class LazyRegistryEntryListField> extends RegistryEntryListField { /** Creates a new field. */ - public LazyRegistryEntryListField( String key, RegistryEntryList defaultValue, String... description ) { + public LazyRegistryEntryListField( String key, RegistryEntryList defaultValue, @Nullable String... description ) { super( key, defaultValue, description ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/RegistryEntryListField.java b/src/main/java/fathertoast/specialmobs/common/config/field/RegistryEntryListField.java index 5d61176..903658d 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/RegistryEntryListField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/RegistryEntryListField.java @@ -31,7 +31,7 @@ public class RegistryEntryListField> extends Ge } /** Creates a new field. */ - public RegistryEntryListField( String key, RegistryEntryList defaultValue, String... description ) { + public RegistryEntryListField( String key, RegistryEntryList defaultValue, @Nullable String... description ) { super( key, defaultValue, description ); } @@ -57,15 +57,18 @@ public class RegistryEntryListField> extends Ge value = new RegistryEntryList<>( this, valueDefault.getRegistry(), TomlHelper.parseStringList( raw ) ); } + + // Convenience methods + /** @return The registry this list draws from. */ - public IForgeRegistry getRegistry() { return value.getRegistry(); } + public IForgeRegistry getRegistry() { return get().getRegistry(); } /** @return The entries in this list. */ - public Set getEntries() { return value.getEntries(); } + public Set getEntries() { return get().getEntries(); } /** @return Returns true if there are no entries in this list. */ - public boolean isEmpty() { return value.isEmpty(); } + public boolean isEmpty() { return get().isEmpty(); } /** @return Returns true if the entry is contained in this list. */ - public boolean contains( T entry ) { return value.contains( entry ); } + public boolean contains( @Nullable T entry ) { return get().contains( entry ); } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/ScaledDoubleField.java b/src/main/java/fathertoast/specialmobs/common/config/field/ScaledDoubleField.java index a2c98a1..8a2085c 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/ScaledDoubleField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/ScaledDoubleField.java @@ -14,13 +14,13 @@ public class ScaledDoubleField extends DoubleField { private double valueScaled; /** Creates a new field that accepts a common range of values. */ - public ScaledDoubleField( String key, double defaultValue, double scale, Range range, String... description ) { + public ScaledDoubleField( String key, double defaultValue, double scale, Range range, @Nullable String... description ) { super( key, defaultValue, range, description ); SCALE = scale; } /** Creates a new field that accepts a specialized range of values. */ - public ScaledDoubleField( String key, double defaultValue, double scale, double min, double max, String... description ) { + public ScaledDoubleField( String key, double defaultValue, double scale, double min, double max, @Nullable String... description ) { super( key, defaultValue, min, max, description ); SCALE = scale; } diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/SqrDoubleField.java b/src/main/java/fathertoast/specialmobs/common/config/field/SqrDoubleField.java index 627a54d..7798f41 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/SqrDoubleField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/SqrDoubleField.java @@ -11,12 +11,12 @@ public class SqrDoubleField extends DoubleField { private double valueSqr; /** Creates a new field that accepts a common range of values. */ - public SqrDoubleField( String key, double defaultValue, Range range, String... description ) { + public SqrDoubleField( String key, double defaultValue, Range range, @Nullable String... description ) { super( key, defaultValue, range, description ); } /** Creates a new field that accepts a specialized range of values. */ - public SqrDoubleField( String key, double defaultValue, double min, double max, String... description ) { + public SqrDoubleField( String key, double defaultValue, double min, double max, @Nullable String... description ) { super( key, defaultValue, min, max, description ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/package-info.java b/src/main/java/fathertoast/specialmobs/common/config/field/package-info.java new file mode 100644 index 0000000..29826ca --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/field/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.config.field; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/file/TomlHelper.java b/src/main/java/fathertoast/specialmobs/common/config/file/TomlHelper.java index c4017e8..53da30d 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/file/TomlHelper.java +++ b/src/main/java/fathertoast/specialmobs/common/config/file/TomlHelper.java @@ -6,10 +6,12 @@ import fathertoast.specialmobs.common.config.field.DoubleField; import fathertoast.specialmobs.common.config.field.IntField; import fathertoast.specialmobs.common.config.util.ConfigUtil; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +@SuppressWarnings( "unused" ) public final class TomlHelper { private TomlHelper() { } // This is a static access only class that cannot be instantiated @@ -32,7 +34,7 @@ public final class TomlHelper { } /** Attempts to convert a string value to a raw non-string toml literal. May or may not be accurate. */ - public static Object parseRaw( String value ) { + public static Object parseRaw( @Nullable String value ) { // Note: It is very important here that the returned value is NOT a string if( value != null && !"".equals( value ) ) { @@ -56,9 +58,9 @@ public final class TomlHelper { } /** Attempts to convert an object to a toml literal. May or may not be accurate. */ - public static String toLiteral( Object value ) { + public static String toLiteral( @Nullable Object value ) { if( value == null ) { - return "null"; + return ""; } else if( value instanceof Enum ) { return "\"" + ((Enum) value).name().toLowerCase() + "\""; @@ -66,21 +68,22 @@ public final class TomlHelper { else if( value instanceof String ) { return "\"" + value + "\""; } - else if( value instanceof Double && ((Double) value).isInfinite() ) { - // Toml infinite literals do not match java; these may also be unsupported in the current version - return (Double) value > 0.0 ? "Inf" : "-Inf"; - } + // Infinite values not supported + //else if( value instanceof Double && ((Double) value).isInfinite() ) { + // // Toml infinite literals do not match java + // return (Double) value > 0.0 ? "Inf" : "-Inf"; + //} else { return value.toString(); } } /** Attempts to convert an object array to a toml literal. May or may not be accurate. */ - public static String toLiteral( Object... values ) { + public static String toLiteral( @Nullable Object... values ) { if( values == null ) { - return "null"; + return ""; } - else if( values.length < 1 ) { + if( values.length < 1 ) { return "[]"; } else { @@ -92,7 +95,7 @@ public final class TomlHelper { public static String literalList( Object... list ) { return literalList( Arrays.asList( list ) ); } /** Attempts to convert an object list to a list of toml literals. May or may not be accurate. */ - public static String literalList( List list ) { + public static String literalList( @Nullable List list ) { if( list == null || list.isEmpty() ) return ""; StringBuilder literals = new StringBuilder(); for( Object obj : list ) { @@ -196,7 +199,7 @@ public final class TomlHelper { public static List splitKey( String key ) { return StringUtils.split( key, '.' ); } /** Combines a toml path into a key. */ - public static String mergePath( List path ) { + public static String mergePath( @Nullable List path ) { if( path == null || path.isEmpty() ) return ""; StringBuilder key = new StringBuilder(); for( String subKey : path ) { @@ -207,15 +210,13 @@ public final class TomlHelper { } /** Convenience method for creating a list of single-line comments (no \n or \r). */ - public static ArrayList newComment( String... lines ) { - return new ArrayList<>( Arrays.asList( lines ) ); - } + public static ArrayList newComment( String... lines ) { return new ArrayList<>( Arrays.asList( lines ) ); } /** Combines an array of objects as a comma-separated string. */ public static String combineList( Object... list ) { return combineList( Arrays.asList( list ) ); } /** Combines a list of objects as a comma-separated string. */ - public static String combineList( List list ) { + public static String combineList( @Nullable List list ) { if( list == null || list.isEmpty() ) return ""; StringBuilder key = new StringBuilder(); for( Object obj : list ) { diff --git a/src/main/java/fathertoast/specialmobs/common/config/file/package-info.java b/src/main/java/fathertoast/specialmobs/common/config/file/package-info.java new file mode 100644 index 0000000..249454b --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/file/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.config.file; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/species/package-info.java b/src/main/java/fathertoast/specialmobs/common/config/species/package-info.java new file mode 100644 index 0000000..fe4acb4 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/species/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.config.species; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/AttributeList.java b/src/main/java/fathertoast/specialmobs/common/config/util/AttributeList.java index a8dc479..1d8df71 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/AttributeList.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/AttributeList.java @@ -5,6 +5,7 @@ import fathertoast.specialmobs.common.config.file.TomlHelper; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.ai.attributes.AttributeModifierMap; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; @@ -33,7 +34,7 @@ public class AttributeList implements IStringArray { /** @return Returns true if this object has the same value as another object. */ @Override - public boolean equals( Object other ) { + public boolean equals( @Nullable Object other ) { if( !(other instanceof AttributeList) ) return false; // Compare by the string list view of the object return toStringList().equals( ((AttributeList) other).toStringList() ); diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/BlockEntry.java b/src/main/java/fathertoast/specialmobs/common/config/util/BlockEntry.java index bb2ecfa..e9a502c 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/BlockEntry.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/BlockEntry.java @@ -32,7 +32,7 @@ public class BlockEntry implements Cloneable { this( block.getBlock() ); // Add all the properties present in the block state - StateBuilder state = new StateBuilder(); + StateBuilder state = new StateBuilder( BLOCK ); for( Property> property : block.getProperties() ) { state.add( property, block.getValue( property ) ); } @@ -45,13 +45,8 @@ public class BlockEntry implements Cloneable { public BlockEntry( AbstractConfigField field, String line ) { // Parse the base block final String[] pair = line.split( "\\[", 2 ); - final ResourceLocation regKey = new ResourceLocation( pair[0] ); - if( !ForgeRegistries.BLOCKS.containsKey( regKey ) ) { - BLOCK = Blocks.AIR; - } - else { - BLOCK = ForgeRegistries.BLOCKS.getValue( regKey ); - } + final Block block = ForgeRegistries.BLOCKS.getValue( new ResourceLocation( pair[0] ) ); + BLOCK = block == null ? Blocks.AIR : block; // We are done constructing if the entry is invalid or does not specify block states if( BLOCK == Blocks.AIR || pair.length < 2 ) { @@ -143,7 +138,7 @@ public class BlockEntry implements Cloneable { final StateContainer stateContainer = block.getStateDefinition(); // Parse the state and build the matcher - final StateBuilder builder = new StateBuilder(); + final StateBuilder builder = new StateBuilder( block ); final String[] properties = stateString.split( "," ); for( String combinedEntry : properties ) { // Parse an individual property key-value pair @@ -271,9 +266,6 @@ public class BlockEntry implements Cloneable { /** Can be used for building default configs to make block entries with target states. */ public StateBuilder( Block block ) { BLOCK = block; } - /** Used by block entries to build target states. Can not be built into a block entry when using this constructor. */ - private StateBuilder() { BLOCK = null; } - /** @return Returns true if this state builder has no properties set. */ @SuppressWarnings( "BooleanMethodIsAlwaysInverted" ) public boolean isEmpty() { return propertiesToMatch.isEmpty(); } @@ -289,7 +281,7 @@ public class BlockEntry implements Cloneable { /** @return Returns a block entry reflecting the current state of this builder. */ public BlockEntry toBlockEntry() { - BlockEntry target = new BlockEntry( BLOCK ); + final BlockEntry target = new BlockEntry( BLOCK ); if( !isEmpty() ) { target.MATCHERS.add( toTargetState() ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/BlockList.java b/src/main/java/fathertoast/specialmobs/common/config/util/BlockList.java index b8fec09..2180d44 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/BlockList.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/BlockList.java @@ -10,6 +10,7 @@ import net.minecraft.block.Blocks; import net.minecraft.util.ResourceLocation; import net.minecraftforge.registries.ForgeRegistries; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -61,13 +62,11 @@ public class BlockList implements IStringArray { /** @return A string representation of this object. */ @Override - public String toString() { - return TomlHelper.toLiteral( PRINT_LIST.toArray() ); - } + public String toString() { return TomlHelper.toLiteral( PRINT_LIST.toArray() ); } /** @return Returns true if this object has the same value as another object. */ @Override - public boolean equals( Object other ) { + public boolean equals( @Nullable Object other ) { if( !(other instanceof BlockList) ) return false; // Compare by the string list view of the object return toStringList().equals( ((BlockList) other).toStringList() ); @@ -109,10 +108,9 @@ public class BlockList implements IStringArray { private void mergeFromNamespace( String namespace ) { for( ResourceLocation regKey : ForgeRegistries.BLOCKS.getKeys() ) { if( regKey.toString().startsWith( namespace ) ) { - BlockEntry entry = new BlockEntry( ForgeRegistries.BLOCKS.getValue( regKey ) ); - if( entry.BLOCK != null && entry.BLOCK != Blocks.AIR ) { - mergeFrom( entry ); - } + final Block block = ForgeRegistries.BLOCKS.getValue( regKey ); + if( block != null && block != Blocks.AIR ) + mergeFrom( new BlockEntry( block ) ); } } } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/ConfigUtil.java b/src/main/java/fathertoast/specialmobs/common/config/util/ConfigUtil.java index 6a00d46..6bb7109 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/ConfigUtil.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/ConfigUtil.java @@ -1,11 +1,19 @@ package fathertoast.specialmobs.common.config.util; import com.electronwill.nightconfig.core.file.FileConfig; +import fathertoast.specialmobs.common.core.SpecialMobs; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent; import net.minecraftforge.fml.loading.FMLPaths; import java.io.File; -public abstract class ConfigUtil { +@Mod.EventBusSubscriber( modid = SpecialMobs.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE ) +public final class ConfigUtil { + /** The current "version" of the dynamic registries. This is incremented each time dynamic registries are loaded. */ + public static byte DYNAMIC_REGISTRY_VERSION; /** The plus or minus symbol (+/-). */ public static final String PLUS_OR_MINUS = "\u00b1"; @@ -44,4 +52,12 @@ public abstract class ConfigUtil { /** @return A string representation of the file from the game directory. */ public static String toRelativePath( File gameFile ) { return FMLPaths.GAMEDIR.get().relativize( gameFile.toPath() ).toString(); } + + /** + * Called when a server (integrated or dedicated) is about to start. + * + * @param event The event data. + */ + @SubscribeEvent( priority = EventPriority.NORMAL ) + public static void onServerAboutToStart( FMLServerAboutToStartEvent event ) { DYNAMIC_REGISTRY_VERSION++; } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/EntityList.java b/src/main/java/fathertoast/specialmobs/common/config/util/EntityList.java index 45e4c17..b790b12 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/EntityList.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/EntityList.java @@ -5,6 +5,7 @@ import fathertoast.specialmobs.common.config.file.TomlHelper; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; @@ -45,7 +46,7 @@ public class EntityList implements IStringArray { /** @return Returns true if this object has the same value as another object. */ @Override - public boolean equals( Object other ) { + public boolean equals( @Nullable Object other ) { if( !(other instanceof EntityList) ) return false; // Compare by the string list view of the object return toStringList().equals( ((EntityList) other).toStringList() ); @@ -63,7 +64,8 @@ public class EntityList implements IStringArray { } /** @return True if the entity is contained in this list. */ - public boolean contains( Entity entity ) { + public boolean contains( @Nullable Entity entity ) { + if( entity == null ) return false; final EntityEntry targetEntry = new EntityEntry( entity ); for( EntityEntry currentEntry : ENTRIES ) { currentEntry.checkClass( entity.level ); @@ -77,7 +79,9 @@ public class EntityList implements IStringArray { * @param entity The entity to retrieve values for. * @return The array of values of the best-match entry. Returns null if the entity is not contained in this entity list. */ - public double[] getValues( Entity entity ) { + @Nullable + public double[] getValues( @Nullable Entity entity ) { + if( entity == null ) return null; final EntityEntry targetEntry = new EntityEntry( entity ); EntityEntry bestMatch = null; for( EntityEntry currentEntry : ENTRIES ) { @@ -101,7 +105,7 @@ public class EntityList implements IStringArray { * @see #setSingleValue() * @see #setSinglePercent() */ - public double getValue( Entity entity ) { + public double getValue( @Nullable Entity entity ) { final double[] values = getValues( entity ); return values == null || values.length < 1 ? 0.0 : values[0]; } @@ -112,7 +116,7 @@ public class EntityList implements IStringArray { * is not contained in this entity list or has no values specified. This should only be used for 'single percent' lists. * @see #setSinglePercent() */ - public boolean rollChance( LivingEntity entity ) { + public boolean rollChance( @Nullable LivingEntity entity ) { return ENTRIES.length > 0 && entity != null && entity.getRandom().nextDouble() < getValue( entity ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java new file mode 100644 index 0000000..6ea47e4 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java @@ -0,0 +1,112 @@ +package fathertoast.specialmobs.common.config.util; + +import fathertoast.specialmobs.common.config.util.environment.*; +import fathertoast.specialmobs.common.config.util.environment.biome.BiomeEnvironment; +import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionTypeEnvironment; +import fathertoast.specialmobs.common.config.util.environment.position.StructureEnvironment; +import fathertoast.specialmobs.common.config.util.environment.position.YEnvironment; +import fathertoast.specialmobs.common.config.util.environment.position.YFromSeaEnvironment; +import net.minecraft.util.RegistryKey; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.DimensionType; +import net.minecraft.world.World; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.gen.feature.structure.Structure; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +/** + * One condition-value entry in an environment list. Uses a 'lazy' implementation so any needed registries are + * not polled until this entry is actually used. + */ +@SuppressWarnings( "unused" ) +public class EnvironmentEntry { + /** The value given to this entry. */ + public final double VALUE; + /** The conditions that define this entry's environment. */ + private final AbstractEnvironment[] CONDITIONS; + + /** Creates an entry with the specified values. */ + public EnvironmentEntry( double value, List conditions ) { this( value, conditions.toArray( new AbstractEnvironment[0] ) ); } + + /** Creates an entry with the specified values. */ + public EnvironmentEntry( double value, AbstractEnvironment... conditions ) { + VALUE = value; + CONDITIONS = conditions; + } + + /** @return Returns true if all this entry's conditions match the provided environment. */ + public boolean matches( World world, @Nullable BlockPos pos ) { + for( AbstractEnvironment condition : CONDITIONS ) { + if( !condition.matches( world, pos ) ) return false; + } + return true; + } + + /** + * @return The string representation of this environment entry, as it would appear in a config file. + *

+ * Format is "value condition1 state1 & condition2 state2 & ...". + */ + @Override + public String toString() { + // Start with the value + final StringBuilder str = new StringBuilder().append( VALUE ).append( ' ' ); + // List all conditions + boolean first = true; + for( AbstractEnvironment condition : CONDITIONS ) { + if( first ) first = false; + else str.append( " & " ); + str.append( condition ); + } + return str.toString(); + } + + public static Builder builder( double value ) { return new Builder( value ); } + + /** + * Builder class used to simplify creation of environment entries, with shortcuts for the most commonly used environments. + */ + public static class Builder { + private final double VALUE; + private final ArrayList CONDITIONS = new ArrayList<>(); + + private Builder( double value ) { VALUE = value; } + + public EnvironmentEntry build() { return new EnvironmentEntry( VALUE, CONDITIONS ); } + + public Builder in( AbstractEnvironment condition ) { + CONDITIONS.add( condition ); + return this; + } + + + // Dimension-based + + public Builder inDimensionType( RegistryKey dimType ) { return in( new DimensionTypeEnvironment( dimType ) ); } + + + // Biome-based + + public Builder inBiome( RegistryKey biome ) { return in( new BiomeEnvironment( biome ) ); } + + + // Position-based + + public Builder inStructure( Structure structure ) { return in( new StructureEnvironment( structure ) ); } + + public Builder belowY( int y ) { return in( new YEnvironment( ComparisonOperator.LESS_THAN, y ) ); } + + public Builder atLeastY( int y ) { return in( new YEnvironment( ComparisonOperator.GREATER_OR_EQUAL, y ) ); } + + public Builder belowSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_THAN, 0 ) ); } + + public Builder atLeastSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.GREATER_OR_EQUAL, 0 ) ); } + + + // Time-based + + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentList.java b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentList.java new file mode 100644 index 0000000..1fc2de7 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentList.java @@ -0,0 +1,100 @@ +package fathertoast.specialmobs.common.config.util; + +import fathertoast.specialmobs.common.config.field.DoubleField; +import fathertoast.specialmobs.common.config.field.IStringArray; +import fathertoast.specialmobs.common.config.file.TomlHelper; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +/** + * A list of condition-value entries used to link one number to specific environments. + */ +@SuppressWarnings( { "unused", "SameParameterValue" } ) +public class EnvironmentList implements IStringArray { + /** The condition-value entries in this list. */ + private final EnvironmentEntry[] ENTRIES; + + /** The minimum value accepted for entry values in this list. */ + private double minValue = Double.NEGATIVE_INFINITY; + /** The maximum value accepted for entry values in this list. */ + private double maxValue = Double.POSITIVE_INFINITY; + + /** + * Create a new environment list from a list of entries. + *

+ * By default, environment list value(s) can be any numerical double. + * This can be changed with helper methods that alter values' bounds and return 'this'. + */ + public EnvironmentList( List entries ) { this( entries.toArray( new EnvironmentEntry[0] ) ); } + + /** + * Create a new environment list from an array of entries. Used for creating default configs. + *

+ * By default, environment list value(s) can be any numerical double. + * This can be changed with helper methods that alter values' bounds and return 'this'. + */ + public EnvironmentList( EnvironmentEntry... entries ) { ENTRIES = entries; } + + /** @return A string representation of this object. */ + @Override + public String toString() { return TomlHelper.toLiteral( toStringList().toArray() ); } + + /** @return Returns true if this object has the same value as another object. */ + @Override + public boolean equals( @Nullable Object other ) { + if( !(other instanceof EnvironmentList) ) return false; + // Compare by the string list view of the object + return toStringList().equals( ((EnvironmentList) other).toStringList() ); + } + + /** @return A list of strings that will represent this object when written to a toml file. */ + @Override + public List toStringList() { + // Create a list of the entries in string format + final List list = new ArrayList<>( ENTRIES.length ); + for( EnvironmentEntry entry : ENTRIES ) { + list.add( entry.toString() ); + } + return list; + } + + /** @return The value matching the given environment, or the default value if no matching environment is defined. */ + public double getOrElse( World world, @Nullable BlockPos pos, DoubleField defaultValue ) { + return getOrElse( world, pos, defaultValue.get() ); + } + + /** @return The value matching the given environment, or the default value if no matching environment is defined. */ + public double getOrElse( World world, @Nullable BlockPos pos, double defaultValue ) { + final Double value = get( world, pos ); + return value == null ? defaultValue : value; + } + + /** @return The value matching the given environment, or null if no matching environment is defined. */ + @Nullable + public Double get( World world, @Nullable BlockPos pos ) { + for( EnvironmentEntry entry : ENTRIES ) { + if( entry.matches( world, pos ) ) return entry.VALUE; + } + return null; + } + + /** Bounds entry values in this list to the specified range. */ + public EnvironmentList setRange( DoubleField.Range range ) { return setRange( range.MIN, range.MAX ); } + + /** Bounds entry values in this list to the specified limits, inclusive. */ + public EnvironmentList setRange( double min, double max ) { + minValue = min; + maxValue = max; + return this; + } + + /** @return The minimum value that can be given to entry values. */ + public double getMinValue() { return minValue; } + + /** @return The maximum value that can be given to entry values. */ + public double getMaxValue() { return maxValue; } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/RegistryEntryList.java b/src/main/java/fathertoast/specialmobs/common/config/util/RegistryEntryList.java index c3ea6dd..f003e80 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/RegistryEntryList.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/RegistryEntryList.java @@ -8,6 +8,7 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.registries.IForgeRegistry; import net.minecraftforge.registries.IForgeRegistryEntry; +import javax.annotation.Nullable; import java.util.*; /** @@ -25,9 +26,7 @@ public class RegistryEntryList> implements IStr /** The list used to write back to file. */ protected final List PRINT_LIST = new ArrayList<>(); - protected RegistryEntryList( IForgeRegistry registry ) { - REGISTRY = registry; - } + protected RegistryEntryList( IForgeRegistry registry ) { REGISTRY = registry; } /** * Create a new registry entry list from an array of entries. Used for creating default configs. @@ -79,13 +78,11 @@ public class RegistryEntryList> implements IStr /** @return A string representation of this object. */ @Override - public String toString() { - return TomlHelper.toLiteral( PRINT_LIST.toArray() ); - } + public String toString() { return TomlHelper.toLiteral( PRINT_LIST.toArray() ); } /** @return Returns true if this object has the same value as another object. */ @Override - public boolean equals( Object other ) { + public boolean equals( @Nullable Object other ) { if( !(other instanceof RegistryEntryList) ) return false; // Compare by the registries used and string list view of the object return getRegistry() == ((RegistryEntryList) other).getRegistry() && @@ -100,7 +97,7 @@ public class RegistryEntryList> implements IStr public boolean isEmpty() { return UNDERLYING_SET.isEmpty(); } /** @return Returns true if the entry is contained in this list. */ - public boolean contains( T entry ) { return UNDERLYING_SET.contains( entry ); } + public boolean contains( @Nullable T entry ) { return UNDERLYING_SET.contains( entry ); } /** @return Adds the registry entry if it exists and isn't already present, returns true if successful. */ protected boolean mergeFrom( ResourceLocation regKey ) { diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/WeightedList.java b/src/main/java/fathertoast/specialmobs/common/config/util/WeightedList.java index e32321c..ed91404 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/WeightedList.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/WeightedList.java @@ -13,6 +13,7 @@ import java.util.*; *

* Creates a config field for each item so weights can be defined by the user. */ +@SuppressWarnings( "unused" ) public class WeightedList { /** The spec used by this config that defines the file's format. */ protected final ToastConfigSpec SPEC; @@ -110,6 +111,7 @@ public class WeightedList { default int getDefaultWeight() { return 1; } /** @return Returns the comment for this object. */ + @Nullable default String[] getComment() { return null; } } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/AbstractEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/AbstractEnvironment.java new file mode 100644 index 0000000..b30a895 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/AbstractEnvironment.java @@ -0,0 +1,21 @@ +package fathertoast.specialmobs.common.config.util.environment; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public abstract class AbstractEnvironment { + /** @return The string representation of this environment, as it would appear in a config file. */ + @Override + public final String toString() { return name() + " " + value(); } + + /** @return The string name of this environment, as it would appear in a config file. */ + public abstract String name(); + + /** @return The string value of this environment, as it would appear in a config file. */ + public abstract String value(); + + /** @return Returns true if this environment matches the provided environment. */ + public abstract boolean matches( World world, @Nullable BlockPos pos ); +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/CompareFloatEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/CompareFloatEnvironment.java new file mode 100644 index 0000000..4e3ef84 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/CompareFloatEnvironment.java @@ -0,0 +1,87 @@ +package fathertoast.specialmobs.common.config.util.environment; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.file.TomlHelper; +import fathertoast.specialmobs.common.core.SpecialMobs; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public abstract class CompareFloatEnvironment extends AbstractEnvironment { + /** How the actual value is compared to this environment's value. */ + public final ComparisonOperator COMPARATOR; + /** The value for this environment. */ + public final float VALUE; + + public CompareFloatEnvironment( ComparisonOperator op, float value ) { + COMPARATOR = op; + VALUE = value; + } + + public CompareFloatEnvironment( AbstractConfigField field, String line ) { + if( line.isEmpty() ) { + COMPARATOR = ComparisonOperator.LESS_THAN; + VALUE = 0.0F; + SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Not defined. Defaulting to \"{}\". Invalid entry: {}", + field.getClass(), field.getKey(), value(), line ); + } + else { + final ComparisonOperator op = ComparisonOperator.parse( line ); + if( op == null ) { + COMPARATOR = ComparisonOperator.LESS_THAN; + SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Comparison not defined (must be in the set [ {} ]). Defaulting to \"{}\". Invalid entry: {}", + field.getClass(), field.getKey(), TomlHelper.literalList( (Object[]) ComparisonOperator.values() ), COMPARATOR, line ); + } + else COMPARATOR = op; + VALUE = parseValue( field, line, line.substring( COMPARATOR.toString().length() ).trim() ); + } + } + + /** @return Parses the value and returns a valid result. */ + private float parseValue( AbstractConfigField field, String line, String arg ) { + // Try to parse the value + float value; + try { + value = Float.parseFloat( arg ); + } + catch( NumberFormatException ex ) { + SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Value not defined (must be a float). Defaulting to '0'. Invalid entry: {}", + field.getClass(), field.getKey(), line ); + value = 0.0F; + } + // Verify value is within range + if( value < getMinValue() ) { + SpecialMobs.LOG.warn( "Value for {} \"{}\" is below the minimum ({})! Clamping value. Invalid value: {}", + field.getClass(), field.getKey(), getMinValue(), value ); + value = getMinValue(); + } + else if( value > getMaxValue() ) { + SpecialMobs.LOG.warn( "Value for {} \"{}\" is above the maximum ({})! Clamping value. Invalid value: {}", + field.getClass(), field.getKey(), getMaxValue(), value ); + value = getMaxValue(); + } + return value; + } + + /** @return The minimum value that can be given to the value. */ + protected float getMinValue() { return Float.NEGATIVE_INFINITY; } + + /** @return The maximum value that can be given to the value. */ + protected float getMaxValue() { return Float.POSITIVE_INFINITY; } + + /** @return The string value of this environment, as it would appear in a config file. */ + @Override + public String value() { return COMPARATOR + " " + VALUE; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public boolean matches( World world, @Nullable BlockPos pos ) { + final float actual = getActual( world, pos ); + return !Float.isNaN( actual ) && COMPARATOR.apply( actual, VALUE ); + } + + /** @return Returns the actual value to compare, or Float.NaN if there isn't enough information. */ + public abstract float getActual( World world, @Nullable BlockPos pos ); + +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/CompareIntEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/CompareIntEnvironment.java new file mode 100644 index 0000000..74fd323 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/CompareIntEnvironment.java @@ -0,0 +1,87 @@ +package fathertoast.specialmobs.common.config.util.environment; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.file.TomlHelper; +import fathertoast.specialmobs.common.core.SpecialMobs; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public abstract class CompareIntEnvironment extends AbstractEnvironment { + /** How the actual value is compared to this environment's value. */ + public final ComparisonOperator COMPARATOR; + /** The value for this environment. */ + public final int VALUE; + + public CompareIntEnvironment( ComparisonOperator op, int value ) { + COMPARATOR = op; + VALUE = value; + } + + public CompareIntEnvironment( AbstractConfigField field, String line ) { + if( line.isEmpty() ) { + COMPARATOR = ComparisonOperator.LESS_THAN; + VALUE = 0; + SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Not defined. Defaulting to \"{}\". Invalid entry: {}", + field.getClass(), field.getKey(), value(), line ); + } + else { + final ComparisonOperator op = ComparisonOperator.parse( line ); + if( op == null ) { + COMPARATOR = ComparisonOperator.LESS_THAN; + SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Comparison not defined (must be in the set [ {} ]). Defaulting to \"{}\". Invalid entry: {}", + field.getClass(), field.getKey(), TomlHelper.literalList( (Object[]) ComparisonOperator.values() ), COMPARATOR, line ); + } + else COMPARATOR = op; + VALUE = parseValue( field, line, line.substring( COMPARATOR.toString().length() ).trim() ); + } + } + + /** @return Parses the value and returns a valid result. */ + private int parseValue( AbstractConfigField field, String line, String arg ) { + // Try to parse the value + int value; + try { + value = Integer.parseInt( arg ); + } + catch( NumberFormatException ex ) { + SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Value not defined (must be an integer). Defaulting to '0'. Invalid entry: {}", + field.getClass(), field.getKey(), line ); + value = 0; + } + // Verify value is within range + if( value < getMinValue() ) { + SpecialMobs.LOG.warn( "Value for {} \"{}\" is below the minimum ({})! Clamping value. Invalid value: {}", + field.getClass(), field.getKey(), getMinValue(), value ); + value = getMinValue(); + } + else if( value > getMaxValue() ) { + SpecialMobs.LOG.warn( "Value for {} \"{}\" is above the maximum ({})! Clamping value. Invalid value: {}", + field.getClass(), field.getKey(), getMaxValue(), value ); + value = getMaxValue(); + } + return value; + } + + /** @return The minimum value that can be given to the value. */ + protected int getMinValue() { return Integer.MIN_VALUE; } + + /** @return The maximum value that can be given to the value. */ + protected int getMaxValue() { return Integer.MAX_VALUE; } + + /** @return The string value of this environment, as it would appear in a config file. */ + @Override + public String value() { return COMPARATOR + " " + VALUE; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public boolean matches( World world, @Nullable BlockPos pos ) { + final Integer actual = getActual( world, pos ); + return actual != null && COMPARATOR.apply( actual, VALUE ); + } + + /** @return Returns the actual value to compare, or null if there isn't enough information. */ + @Nullable + public abstract Integer getActual( World world, @Nullable BlockPos pos ); +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/CompareLongEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/CompareLongEnvironment.java new file mode 100644 index 0000000..e8bd88d --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/CompareLongEnvironment.java @@ -0,0 +1,87 @@ +package fathertoast.specialmobs.common.config.util.environment; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.file.TomlHelper; +import fathertoast.specialmobs.common.core.SpecialMobs; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public abstract class CompareLongEnvironment extends AbstractEnvironment { + /** How the actual value is compared to this environment's value. */ + public final ComparisonOperator COMPARATOR; + /** The value for this environment. */ + public final long VALUE; + + public CompareLongEnvironment( ComparisonOperator op, long value ) { + COMPARATOR = op; + VALUE = value; + } + + public CompareLongEnvironment( AbstractConfigField field, String line ) { + if( line.isEmpty() ) { + COMPARATOR = ComparisonOperator.LESS_THAN; + VALUE = 0L; + SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Not defined. Defaulting to \"{}\". Invalid entry: {}", + field.getClass(), field.getKey(), value(), line ); + } + else { + final ComparisonOperator op = ComparisonOperator.parse( line ); + if( op == null ) { + COMPARATOR = ComparisonOperator.LESS_THAN; + SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Comparison not defined (must be in the set [ {} ]). Defaulting to \"{}\". Invalid entry: {}", + field.getClass(), field.getKey(), TomlHelper.literalList( (Object[]) ComparisonOperator.values() ), COMPARATOR, line ); + } + else COMPARATOR = op; + VALUE = parseValue( field, line, line.substring( COMPARATOR.toString().length() ).trim() ); + } + } + + /** @return Parses the value and returns a valid result. */ + private long parseValue( AbstractConfigField field, String line, String arg ) { + // Try to parse the value + long value; + try { + value = Long.parseLong( arg ); + } + catch( NumberFormatException ex ) { + SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Value not defined (must be a long). Defaulting to '0'. Invalid entry: {}", + field.getClass(), field.getKey(), line ); + value = 0; + } + // Verify value is within range + if( value < getMinValue() ) { + SpecialMobs.LOG.warn( "Value for {} \"{}\" is below the minimum ({})! Clamping value. Invalid value: {}", + field.getClass(), field.getKey(), getMinValue(), value ); + value = getMinValue(); + } + else if( value > getMaxValue() ) { + SpecialMobs.LOG.warn( "Value for {} \"{}\" is above the maximum ({})! Clamping value. Invalid value: {}", + field.getClass(), field.getKey(), getMaxValue(), value ); + value = getMaxValue(); + } + return value; + } + + /** @return The minimum value that can be given to the value. */ + protected long getMinValue() { return Long.MIN_VALUE; } + + /** @return The maximum value that can be given to the value. */ + protected long getMaxValue() { return Long.MAX_VALUE; } + + /** @return The string value of this environment, as it would appear in a config file. */ + @Override + public String value() { return COMPARATOR + " " + VALUE; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public boolean matches( World world, @Nullable BlockPos pos ) { + final Long actual = getActual( world, pos ); + return actual != null && COMPARATOR.apply( actual, VALUE ); + } + + /** @return Returns the actual value to compare, or null if there isn't enough information. */ + @Nullable + public abstract Long getActual( World world, @Nullable BlockPos pos ); +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/ComparisonOperator.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/ComparisonOperator.java new file mode 100644 index 0000000..9744dc0 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/ComparisonOperator.java @@ -0,0 +1,78 @@ +package fathertoast.specialmobs.common.config.util.environment; + +import javax.annotation.Nullable; + +public enum ComparisonOperator { + NOT_EQUAL_TO( "!=" ), LESS_OR_EQUAL( "<=" ), GREATER_OR_EQUAL( ">=" ), + EQUAL_TO( "=" ), LESS_THAN( "<" ), GREATER_THAN( ">" ); + + private final String LITERAL; + + ComparisonOperator( String str ) { LITERAL = str; } + + @Override + public String toString() { return LITERAL; } + + public boolean apply( float first, float second ) { + switch( this ) { + case LESS_THAN: + return first < second; + case LESS_OR_EQUAL: + return first <= second; + case GREATER_THAN: + return first > second; + case GREATER_OR_EQUAL: + return first >= second; + case EQUAL_TO: + return first == second; + case NOT_EQUAL_TO: + return first != second; + } + throw new IllegalStateException( "Float comparison implementation is invalid! :(" ); + } + + public boolean apply( int first, int second ) { + switch( this ) { + case LESS_THAN: + return first < second; + case LESS_OR_EQUAL: + return first <= second; + case GREATER_THAN: + return first > second; + case GREATER_OR_EQUAL: + return first >= second; + case EQUAL_TO: + return first == second; + case NOT_EQUAL_TO: + return first != second; + } + throw new IllegalStateException( "Integer comparison implementation is invalid! :(" ); + } + + public boolean apply( long first, long second ) { + switch( this ) { + case LESS_THAN: + return first < second; + case LESS_OR_EQUAL: + return first <= second; + case GREATER_THAN: + return first > second; + case GREATER_OR_EQUAL: + return first >= second; + case EQUAL_TO: + return first == second; + case NOT_EQUAL_TO: + return first != second; + } + throw new IllegalStateException( "Long comparison implementation is invalid! :(" ); + } + + /** @return The operator described by a given string, or null if invalid. */ + @Nullable + public static ComparisonOperator parse( String op ) { + for( ComparisonOperator operator : values() ) { + if( op.startsWith( operator.LITERAL ) ) return operator; + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryEnvironment.java new file mode 100644 index 0000000..2b19374 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryEnvironment.java @@ -0,0 +1,78 @@ +package fathertoast.specialmobs.common.config.util.environment; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.util.ConfigUtil; +import fathertoast.specialmobs.common.core.SpecialMobs; +import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; + +import javax.annotation.Nullable; + +/** + * Dynamic registries are contained in {@link net.minecraft.util.registry.DynamicRegistries} + */ +public abstract class DynamicRegistryEnvironment extends AbstractEnvironment { + /** The field containing this entry. We save a reference to help improve error/warning reports. */ + private final AbstractConfigField FIELD; + + /** If true, the condition is inverted. */ + protected final boolean INVERT; + /** The registry key for this environment. */ + private final ResourceLocation REGISTRY_KEY; + + private T registryEntry; + /** The value of ConfigUtil#DYNAMIC_REGISTRY_VERSION at the time of last poll. */ + private byte version = -1; + + public DynamicRegistryEnvironment( ResourceLocation regKey ) { this( regKey, false ); } + + public DynamicRegistryEnvironment( ResourceLocation regKey, boolean invert ) { + FIELD = null; + INVERT = invert; + REGISTRY_KEY = regKey; + } + + public DynamicRegistryEnvironment( AbstractConfigField field, String line ) { + FIELD = field; + INVERT = line.startsWith( "!" ); + REGISTRY_KEY = new ResourceLocation( INVERT ? line.substring( 1 ) : line ); + } + + /** @return The string value of this environment, as it would appear in a config file. */ + @Override + public final String value() { return (INVERT ? "!" : "") + REGISTRY_KEY.toString(); } + + /** @return The registry used. */ + public abstract RegistryKey> getRegistry(); + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public final boolean matches( World world, @Nullable BlockPos pos ) { + if( world instanceof ServerWorld ) + return matches( (ServerWorld) world, pos ); // These don't work on the client :( + return INVERT; + } + + /** @return Returns true if this environment matches the provided environment. */ + public abstract boolean matches( ServerWorld world, @Nullable BlockPos pos ); + + /** @return The target registry object. */ + @Nullable + public final T getRegistryEntry( ServerWorld world ) { + if( version != ConfigUtil.DYNAMIC_REGISTRY_VERSION ) { + version = ConfigUtil.DYNAMIC_REGISTRY_VERSION; + + final Registry registry = world.getServer().registryAccess().registryOrThrow( getRegistry() ); + registryEntry = registry.get( REGISTRY_KEY ); + if( registryEntry == null ) { + SpecialMobs.LOG.info( "Missing entry for {} \"{}\"! Not present in registry \"{}\". Missing entry: {}", + FIELD.getClass(), FIELD.getKey(), getRegistry().getRegistryName(), REGISTRY_KEY ); + } + } + return registryEntry; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryGroupEnvironment.java new file mode 100644 index 0000000..c39987d --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryGroupEnvironment.java @@ -0,0 +1,87 @@ +package fathertoast.specialmobs.common.config.util.environment; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.util.ConfigUtil; +import fathertoast.specialmobs.common.core.SpecialMobs; +import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Dynamic registries are contained in {@link net.minecraft.util.registry.DynamicRegistries} + */ +public abstract class DynamicRegistryGroupEnvironment extends AbstractEnvironment { + /** The field containing this entry. We save a reference to help improve error/warning reports. */ + private final AbstractConfigField FIELD; + + /** If true, the condition is inverted. */ + protected final boolean INVERT; + /** The namespace for this environment. */ + private final String NAMESPACE; + + private List registryEntries; + /** The value of ConfigUtil#DYNAMIC_REGISTRY_VERSION at the time of last poll. */ + private byte version = -1; + + public DynamicRegistryGroupEnvironment( ResourceLocation regKey ) { this( regKey, false ); } + + public DynamicRegistryGroupEnvironment( ResourceLocation regKey, boolean invert ) { + FIELD = null; + INVERT = invert; + NAMESPACE = regKey.toString(); + } + + public DynamicRegistryGroupEnvironment( AbstractConfigField field, String line ) { + FIELD = field; + INVERT = line.startsWith( "!" ); + NAMESPACE = line.substring( INVERT ? 1 : 0, line.length() - 1 ); + } + + /** @return The string value of this environment, as it would appear in a config file. */ + @Override + public final String value() { return (INVERT ? "!" : "") + NAMESPACE + "*"; } + + /** @return The registry used. */ + public abstract RegistryKey> getRegistry(); + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public final boolean matches( World world, @Nullable BlockPos pos ) { + if( world instanceof ServerWorld ) + return matches( (ServerWorld) world, pos ); // These don't work on the client :( + return INVERT; + } + + /** @return Returns true if this environment matches the provided environment. */ + public abstract boolean matches( ServerWorld world, @Nullable BlockPos pos ); + + /** @return The target registry object. */ + protected final List getRegistryEntries( ServerWorld world ) { + if( version != ConfigUtil.DYNAMIC_REGISTRY_VERSION ) { + version = ConfigUtil.DYNAMIC_REGISTRY_VERSION; + + registryEntries = new ArrayList<>(); + final Registry registry = world.getServer().registryAccess().registryOrThrow( getRegistry() ); + for( ResourceLocation regKey : registry.keySet() ) { + if( regKey.toString().startsWith( NAMESPACE ) ) { + final T entry = registry.get( regKey ); + if( entry != null ) registryEntries.add( entry ); + } + } + if( registryEntries.isEmpty() ) { + SpecialMobs.LOG.info( "Namespace entry for {} \"{}\" did not match anything in registry \"{}\"! Questionable entry: {}", + FIELD == null ? "DEFAULT" : FIELD.getClass(), FIELD == null ? "DEFAULT" : FIELD.getKey(), getRegistry().getRegistryName(), NAMESPACE ); + } + registryEntries = Collections.unmodifiableList( registryEntries ); + } + return registryEntries; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/EnumEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/EnumEnvironment.java new file mode 100644 index 0000000..df3ab48 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/EnumEnvironment.java @@ -0,0 +1,41 @@ +package fathertoast.specialmobs.common.config.util.environment; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.file.TomlHelper; +import fathertoast.specialmobs.common.core.SpecialMobs; + +import java.util.Locale; + +public abstract class EnumEnvironment> extends AbstractEnvironment { + /** If true, the condition is inverted. */ + protected final boolean INVERT; + /** The enum value for this environment. */ + protected final T VALUE; + + public EnumEnvironment( T value ) { this( value, false ); } + + public EnumEnvironment( T value, boolean invert ) { + INVERT = invert; + VALUE = value; + } + + public EnumEnvironment( AbstractConfigField field, String line, T[] validValues ) { + INVERT = line.startsWith( "!" ); + VALUE = parseValue( field, line, validValues, INVERT ? line.substring( 1 ) : line ); + } + + /** @return Attempts to parse the string literal as one of the valid values and returns it, or null if invalid. */ + private T parseValue( AbstractConfigField field, String line, T[] validValues, String name ) { + for( T value : validValues ) { + if( value.name().equalsIgnoreCase( name ) ) return value; + } + // Value cannot be parsed + SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Value not defined (must be in the set [ {} ]). Defaulting to {}. Invalid entry: {}", + field.getClass(), field.getKey(), TomlHelper.literalList( (Object[]) validValues ), TomlHelper.toLiteral(), line ); + return validValues[0]; + } + + /** @return The string value of this environment, as it would appear in a config file. */ + @Override + public final String value() { return (INVERT ? "!" : "") + VALUE.name().toLowerCase( Locale.ROOT ); } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryEnvironment.java new file mode 100644 index 0000000..96db0d1 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryEnvironment.java @@ -0,0 +1,59 @@ +package fathertoast.specialmobs.common.config.util.environment; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.core.SpecialMobs; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.IForgeRegistryEntry; + +import javax.annotation.Nullable; + +/** + * Registries are contained in {@link net.minecraftforge.registries.ForgeRegistries} + */ +public abstract class RegistryEnvironment> extends AbstractEnvironment { + /** The field containing this entry. We save a reference to help improve error/warning reports. */ + private final AbstractConfigField FIELD; + + /** If true, the condition is inverted. */ + protected final boolean INVERT; + /** The registry key for this environment. */ + private final ResourceLocation REGISTRY_KEY; + + private T registryEntry; + + public RegistryEnvironment( T regEntry ) { this( regEntry, false ); } + + public RegistryEnvironment( T regEntry, boolean invert ) { + FIELD = null; + INVERT = invert; + REGISTRY_KEY = regEntry.getRegistryName(); + registryEntry = regEntry; + } + + public RegistryEnvironment( AbstractConfigField field, String line ) { + FIELD = field; + INVERT = line.startsWith( "!" ); + REGISTRY_KEY = new ResourceLocation( INVERT ? line.substring( 1 ) : line ); + } + + /** @return The string value of this environment, as it would appear in a config file. */ + @Override + public final String value() { return (INVERT ? "!" : "") + REGISTRY_KEY.toString(); } + + /** @return The registry used. */ + public abstract IForgeRegistry getRegistry(); + + /** @return The registry entry. */ + @Nullable + protected final T getRegistryEntry() { + if( registryEntry == null ) { + if( !getRegistry().containsKey( REGISTRY_KEY ) ) { + SpecialMobs.LOG.warn( "Invalid entry for {} \"{}\"! Not present in registry \"{}\". Invalid entry: {}", + FIELD.getClass(), FIELD.getKey(), getRegistry().getRegistryName(), REGISTRY_KEY ); + } + registryEntry = getRegistry().getValue( REGISTRY_KEY ); + } + return registryEntry; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryGroupEnvironment.java new file mode 100644 index 0000000..6c436b1 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryGroupEnvironment.java @@ -0,0 +1,67 @@ +package fathertoast.specialmobs.common.config.util.environment; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.core.SpecialMobs; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.IForgeRegistryEntry; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Registries are contained in {@link net.minecraftforge.registries.ForgeRegistries} + */ +public abstract class RegistryGroupEnvironment> extends AbstractEnvironment { + /** The field containing this entry. We save a reference to help improve error/warning reports. */ + private final AbstractConfigField FIELD; + + /** If true, the condition is inverted. */ + protected final boolean INVERT; + /** The namespace for this environment. */ + private final String NAMESPACE; + + private List registryEntries; + + public RegistryGroupEnvironment( T regEntry ) { this( regEntry, false ); } + + public RegistryGroupEnvironment( T regEntry, boolean invert ) { + FIELD = null; + INVERT = invert; + //noinspection ConstantConditions + NAMESPACE = regEntry.getRegistryName().toString(); + } + + public RegistryGroupEnvironment( AbstractConfigField field, String line ) { + FIELD = field; + INVERT = line.startsWith( "!" ); + NAMESPACE = line.substring( INVERT ? 1 : 0, line.length() - 1 ); + } + + /** @return The string value of this environment, as it would appear in a config file. */ + @Override + public final String value() { return (INVERT ? "!" : "") + NAMESPACE + "*"; } + + /** @return The registry used. */ + public abstract IForgeRegistry getRegistry(); + + /** @return The registry entries. */ + protected final List getRegistryEntries() { + if( registryEntries == null ) { + registryEntries = new ArrayList<>(); + for( ResourceLocation regKey : getRegistry().getKeys() ) { + if( regKey.toString().startsWith( NAMESPACE ) ) { + final T entry = getRegistry().getValue( regKey ); + if( entry != null ) registryEntries.add( entry ); + } + } + if( registryEntries.isEmpty() ) { + SpecialMobs.LOG.warn( "Namespace entry for {} \"{}\" did not match anything in registry \"{}\"! Questionable entry: {}", + FIELD == null ? "DEFAULT" : FIELD.getClass(), FIELD == null ? "DEFAULT" : FIELD.getKey(), getRegistry().getRegistryName(), NAMESPACE ); + } + registryEntries = Collections.unmodifiableList( registryEntries ); + } + return registryEntries; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategoryEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategoryEnvironment.java new file mode 100644 index 0000000..21d7fc9 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategoryEnvironment.java @@ -0,0 +1,52 @@ +package fathertoast.specialmobs.common.config.util.environment.biome; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.EnumEnvironment; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.biome.Biome; + +import javax.annotation.Nullable; + +public class BiomeCategoryEnvironment extends EnumEnvironment { + public enum Value { + NONE( Biome.Category.NONE ), + TAIGA( Biome.Category.TAIGA ), + EXTREME_HILLS( Biome.Category.EXTREME_HILLS ), + JUNGLE( Biome.Category.JUNGLE ), + MESA( Biome.Category.MESA ), + PLAINS( Biome.Category.PLAINS ), + SAVANNA( Biome.Category.SAVANNA ), + ICY( Biome.Category.ICY ), + THE_END( Biome.Category.THEEND ), + BEACH( Biome.Category.BEACH ), + FOREST( Biome.Category.FOREST ), + OCEAN( Biome.Category.OCEAN ), + DESERT( Biome.Category.DESERT ), + RIVER( Biome.Category.RIVER ), + SWAMP( Biome.Category.SWAMP ), + MUSHROOM( Biome.Category.MUSHROOM ), + NETHER( Biome.Category.NETHER ); + + public final Biome.Category BASE; + + Value( Biome.Category vanillaCat ) { BASE = vanillaCat; } + } + + public BiomeCategoryEnvironment( Value value ) { super( value ); } + + public BiomeCategoryEnvironment( Value value, boolean invert ) { super( value, invert ); } + + public BiomeCategoryEnvironment( AbstractConfigField field, String line ) { super( field, line, Value.values() ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_BIOME_CATEGORY; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public boolean matches( World world, @Nullable BlockPos pos ) { + return (pos != null && VALUE.BASE.equals( world.getBiome( pos ).getBiomeCategory() )) != INVERT; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeEnvironment.java new file mode 100644 index 0000000..e2ce149 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeEnvironment.java @@ -0,0 +1,36 @@ +package fathertoast.specialmobs.common.config.util.environment.biome; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.DynamicRegistryEnvironment; +import net.minecraft.util.RegistryKey; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.server.ServerWorld; + +import javax.annotation.Nullable; + +public class BiomeEnvironment extends DynamicRegistryEnvironment { + + public BiomeEnvironment( RegistryKey biome ) { super( biome.getRegistryName() ); } + + public BiomeEnvironment( RegistryKey biome, boolean invert ) { super( biome.getRegistryName(), invert ); } + + public BiomeEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_BIOME; } + + /** @return The registry used. */ + @Override + public RegistryKey> getRegistry() { return Registry.BIOME_REGISTRY; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public boolean matches( ServerWorld world, @Nullable BlockPos pos ) { + final Biome entry = getRegistryEntry( world ); + return (entry != null && pos != null && entry.equals( world.getBiome( pos ) )) != INVERT; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeGroupEnvironment.java new file mode 100644 index 0000000..34c5398 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeGroupEnvironment.java @@ -0,0 +1,43 @@ +package fathertoast.specialmobs.common.config.util.environment.biome; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.DynamicRegistryGroupEnvironment; +import net.minecraft.util.RegistryKey; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.server.ServerWorld; + +import javax.annotation.Nullable; +import java.util.List; + +public class BiomeGroupEnvironment extends DynamicRegistryGroupEnvironment { + + public BiomeGroupEnvironment( RegistryKey biome ) { super( biome.getRegistryName() ); } + + public BiomeGroupEnvironment( RegistryKey biome, boolean invert ) { super( biome.getRegistryName(), invert ); } + + public BiomeGroupEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_BIOME; } + + /** @return The registry used. */ + @Override + public RegistryKey> getRegistry() { return Registry.BIOME_REGISTRY; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public final boolean matches( ServerWorld world, @Nullable BlockPos pos ) { + final Biome target = pos == null ? null : world.getBiome( pos ); + if( target != null ) { + final List entries = getRegistryEntries( world ); + for( Biome entry : entries ) { + if( entry.equals( target ) ) return !INVERT; + } + } + return INVERT; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeTemperatureEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeTemperatureEnvironment.java new file mode 100644 index 0000000..a49182d --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeTemperatureEnvironment.java @@ -0,0 +1,37 @@ +package fathertoast.specialmobs.common.config.util.environment.biome; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.CompareFloatEnvironment; +import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class BiomeTemperatureEnvironment extends CompareFloatEnvironment { + + public BiomeTemperatureEnvironment( ComparisonOperator op, float value ) { super( op, value ); } + + public BiomeTemperatureEnvironment( AbstractConfigField field, String line ) { super( field, TemperatureEnvironment.handleTempInput( line ) ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_BIOME_TEMPERATURE; } + + /** @return The string value of this environment, as it would appear in a config file. */ + @Override + public String value() { + if( COMPARATOR == ComparisonOperator.LESS_THAN && VALUE == 0.15F ) + return TemperatureEnvironment.FREEZING; + if( COMPARATOR == ComparisonOperator.GREATER_THAN && VALUE == Math.nextDown( 0.15F ) ) + return "!" + TemperatureEnvironment.FREEZING; + return super.value(); + } + + /** @return Returns the actual value to compare, or Float.NaN if there isn't enough information. */ + @Override + public float getActual( World world, @Nullable BlockPos pos ) { + return pos == null ? Float.NaN : world.getBiome( pos ).getBaseTemperature(); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/RainfallEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/RainfallEnvironment.java new file mode 100644 index 0000000..71504d9 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/RainfallEnvironment.java @@ -0,0 +1,37 @@ +package fathertoast.specialmobs.common.config.util.environment.biome; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.CompareFloatEnvironment; +import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.biome.Biome; + +import javax.annotation.Nullable; + +public class RainfallEnvironment extends CompareFloatEnvironment { + + public RainfallEnvironment( ComparisonOperator op, float value ) { super( op, value ); } + + public RainfallEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_RAINFALL; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public boolean matches( World world, @Nullable BlockPos pos ) { + // Handle the special case of no rainfall + if( COMPARATOR == ComparisonOperator.EQUAL_TO && VALUE == 0.0F ) + return pos != null && world.getBiome( pos ).getPrecipitation() == Biome.RainType.NONE; + return super.matches( world, pos ); + } + + /** @return Returns the actual value to compare, or Float.NaN if there isn't enough information. */ + @Override + public float getActual( World world, @Nullable BlockPos pos ) { + return pos == null ? Float.NaN : world.getBiome( pos ).getDownfall(); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TemperatureEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TemperatureEnvironment.java new file mode 100644 index 0000000..9b90196 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TemperatureEnvironment.java @@ -0,0 +1,47 @@ +package fathertoast.specialmobs.common.config.util.environment.biome; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.CompareFloatEnvironment; +import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class TemperatureEnvironment extends CompareFloatEnvironment { + + public static final String FREEZING = "freezing"; + + public static String handleTempInput( String line ) { + if( line.equalsIgnoreCase( FREEZING ) ) + return ComparisonOperator.LESS_THAN + " " + 0.15F; + if( line.equalsIgnoreCase( "!" + FREEZING ) ) + return ComparisonOperator.GREATER_THAN + " " + Math.nextDown( 0.15F ); + return line; + } + + public TemperatureEnvironment( ComparisonOperator op, float value ) { super( op, value ); } + + public TemperatureEnvironment( AbstractConfigField field, String line ) { super( field, handleTempInput( line ) ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_TEMPERATURE; } + + /** @return The string value of this environment, as it would appear in a config file. */ + @Override + public String value() { + if( COMPARATOR == ComparisonOperator.LESS_THAN && VALUE == 0.15F ) + return FREEZING; + if( COMPARATOR == ComparisonOperator.GREATER_THAN && VALUE == Math.nextDown( 0.15F ) ) + return "!" + FREEZING; + return super.value(); + } + + /** @return Returns the actual value to compare, or Float.NaN if there isn't enough information. */ + @Override + public float getActual( World world, @Nullable BlockPos pos ) { + return pos == null ? Float.NaN : world.getBiome( pos ).getTemperature( pos ); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/package-info.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/package-info.java new file mode 100644 index 0000000..9c63dd4 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.config.util.environment.biome; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionPropertyEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionPropertyEnvironment.java new file mode 100644 index 0000000..ebd9a32 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionPropertyEnvironment.java @@ -0,0 +1,51 @@ +package fathertoast.specialmobs.common.config.util.environment.dimension; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.EnumEnvironment; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.DimensionType; +import net.minecraft.world.World; + +import javax.annotation.Nullable; +import java.util.function.Function; + +public class DimensionPropertyEnvironment extends EnumEnvironment { + /** + * Represents all boolean values defined by dimension type, named to match data pack format. + * + * @see Data pack format (Minecraft Wiki) + */ + public enum Value { + @SuppressWarnings( "SpellCheckingInspection" ) + ULTRAWARM( DimensionType::ultraWarm ), + NATURAL( DimensionType::natural ), + HAS_SKYLIGHT( DimensionType::hasSkyLight ), + HAS_CEILING( DimensionType::hasCeiling ), + FIXED_TIME( DimensionType::hasFixedTime ), + PIGLIN_SAFE( DimensionType::piglinSafe ), + BED_WORKS( DimensionType::bedWorks ), + RESPAWN_ANCHOR_WORKS( DimensionType::respawnAnchorWorks ), + HAS_RAIDS( DimensionType::hasRaids ); + + private final Function SUPPLIER; + + Value( Function supplier ) { SUPPLIER = supplier; } + + public boolean of( DimensionType dimType ) { return SUPPLIER.apply( dimType ); } + } + + public DimensionPropertyEnvironment( Value value ) { super( value ); } + + public DimensionPropertyEnvironment( Value value, boolean invert ) { super( value, invert ); } + + public DimensionPropertyEnvironment( AbstractConfigField field, String line ) { super( field, line, Value.values() ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_DIMENSION_PROPERTY; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public boolean matches( World world, @Nullable BlockPos pos ) { return VALUE.of( world.dimensionType() ) != INVERT; } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeEnvironment.java new file mode 100644 index 0000000..77d5c9b --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeEnvironment.java @@ -0,0 +1,36 @@ +package fathertoast.specialmobs.common.config.util.environment.dimension; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.DynamicRegistryEnvironment; +import net.minecraft.util.RegistryKey; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.DimensionType; +import net.minecraft.world.server.ServerWorld; + +import javax.annotation.Nullable; + +public class DimensionTypeEnvironment extends DynamicRegistryEnvironment { + + public DimensionTypeEnvironment( RegistryKey dimType ) { super( dimType.getRegistryName() ); } + + public DimensionTypeEnvironment( RegistryKey dimType, boolean invert ) { super( dimType.getRegistryName(), invert ); } + + public DimensionTypeEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_DIMENSION_TYPE; } + + /** @return The registry used. */ + @Override + public RegistryKey> getRegistry() { return Registry.DIMENSION_TYPE_REGISTRY; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public boolean matches( ServerWorld world, @Nullable BlockPos pos ) { + final DimensionType entry = getRegistryEntry( world ); + return (entry != null && entry.equals( world.dimensionType() )) != INVERT; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeGroupEnvironment.java new file mode 100644 index 0000000..9af3e68 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeGroupEnvironment.java @@ -0,0 +1,41 @@ +package fathertoast.specialmobs.common.config.util.environment.dimension; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.DynamicRegistryGroupEnvironment; +import net.minecraft.util.RegistryKey; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.DimensionType; +import net.minecraft.world.server.ServerWorld; + +import javax.annotation.Nullable; +import java.util.List; + +public class DimensionTypeGroupEnvironment extends DynamicRegistryGroupEnvironment { + + public DimensionTypeGroupEnvironment( RegistryKey dimType ) { super( dimType.getRegistryName() ); } + + public DimensionTypeGroupEnvironment( RegistryKey dimType, boolean invert ) { super( dimType.getRegistryName(), invert ); } + + public DimensionTypeGroupEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_DIMENSION_TYPE; } + + /** @return The registry used. */ + @Override + public RegistryKey> getRegistry() { return Registry.DIMENSION_TYPE_REGISTRY; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public final boolean matches( ServerWorld world, @Nullable BlockPos pos ) { + final DimensionType target = world.dimensionType(); + final List entries = getRegistryEntries( world ); + for( DimensionType entry : entries ) { + if( entry.equals( target ) ) return !INVERT; + } + return INVERT; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/package-info.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/package-info.java new file mode 100644 index 0000000..6b06803 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.config.util.environment.dimension; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/package-info.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/package-info.java new file mode 100644 index 0000000..144a8be --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.config.util.environment; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureEnvironment.java new file mode 100644 index 0000000..9ac3202 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureEnvironment.java @@ -0,0 +1,38 @@ +package fathertoast.specialmobs.common.config.util.environment.position; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.RegistryEnvironment; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.structure.Structure; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.IForgeRegistry; + +import javax.annotation.Nullable; + +public class StructureEnvironment extends RegistryEnvironment> { + + public StructureEnvironment( Structure structure ) { super( structure ); } + + public StructureEnvironment( Structure structure, boolean invert ) { super( structure, invert ); } + + public StructureEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_STRUCTURE; } + + /** @return The registry used. */ + @Override + public IForgeRegistry> getRegistry() { return ForgeRegistries.STRUCTURE_FEATURES; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public boolean matches( World world, @Nullable BlockPos pos ) { + final Structure entry = getRegistryEntry(); + return (entry != null && pos != null && world instanceof ServerWorld && + ((ServerWorld) world).structureFeatureManager().getStructureAt( pos, false, entry ).isValid()) != INVERT; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureGroupEnvironment.java new file mode 100644 index 0000000..584ee56 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureGroupEnvironment.java @@ -0,0 +1,46 @@ +package fathertoast.specialmobs.common.config.util.environment.position; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.RegistryGroupEnvironment; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.structure.Structure; +import net.minecraft.world.gen.feature.structure.StructureManager; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.IForgeRegistry; + +import javax.annotation.Nullable; +import java.util.List; + +public class StructureGroupEnvironment extends RegistryGroupEnvironment> { + + public StructureGroupEnvironment( Structure biome ) { super( biome ); } + + public StructureGroupEnvironment( Structure biome, boolean invert ) { super( biome, invert ); } + + public StructureGroupEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_STRUCTURE; } + + /** @return The registry used. */ + @Override + public IForgeRegistry> getRegistry() { return ForgeRegistries.STRUCTURE_FEATURES; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public final boolean matches( World world, @Nullable BlockPos pos ) { + final StructureManager structureManager = pos != null && world instanceof ServerWorld ? + ((ServerWorld) world).structureFeatureManager() : null; + if( structureManager != null ) { + final List> entries = getRegistryEntries(); + for( Structure entry : entries ) { + if( structureManager.getStructureAt( pos, false, entry ).isValid() ) return !INVERT; + } + } + return INVERT; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/YEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/YEnvironment.java new file mode 100644 index 0000000..a51606b --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/YEnvironment.java @@ -0,0 +1,25 @@ +package fathertoast.specialmobs.common.config.util.environment.position; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.CompareIntEnvironment; +import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class YEnvironment extends CompareIntEnvironment { + + public YEnvironment( ComparisonOperator op, int value ) { super( op, value ); } + + public YEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_Y; } + + /** @return Returns the actual value to compare, or null if there isn't enough information. */ + @Override + public Integer getActual( World world, @Nullable BlockPos pos ) { return pos == null ? null : pos.getY(); } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/YFromSeaEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/YFromSeaEnvironment.java new file mode 100644 index 0000000..3f60e19 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/YFromSeaEnvironment.java @@ -0,0 +1,25 @@ +package fathertoast.specialmobs.common.config.util.environment.position; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.CompareIntEnvironment; +import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class YFromSeaEnvironment extends CompareIntEnvironment { + + public YFromSeaEnvironment( ComparisonOperator op, int value ) { super( op, value ); } + + public YFromSeaEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_Y_FROM_SEA; } + + /** @return Returns the actual value to compare, or null if there isn't enough information. */ + @Override + public Integer getActual( World world, @Nullable BlockPos pos ) { return pos == null ? null : pos.getY() - world.getSeaLevel(); } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/package-info.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/package-info.java new file mode 100644 index 0000000..817d9d0 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.config.util.environment.position; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DayTimeEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DayTimeEnvironment.java new file mode 100644 index 0000000..98f038d --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DayTimeEnvironment.java @@ -0,0 +1,45 @@ +package fathertoast.specialmobs.common.config.util.environment.time; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.EnumEnvironment; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class DayTimeEnvironment extends EnumEnvironment { + /** Values match up to the vanilla set time command. */ + public enum Value { + DAY( 1_000, 13_000 ), SUNSET( 12_000, 13_000 ), + NIGHT( 13_000, 1_000 ), SUNRISE( 23_000, 1_000 ); + + private final int START, END; + + Value( int start, int end ) { + START = start; + END = end; + } + + public boolean matches( int dayTime ) { + if( START < END ) return START <= dayTime && dayTime < END; + return START <= dayTime || dayTime < END; // Handle day wrapping + } + } + + public DayTimeEnvironment( Value value ) { super( value ); } + + public DayTimeEnvironment( Value value, boolean invert ) { super( value, invert ); } + + public DayTimeEnvironment( AbstractConfigField field, String line ) { super( field, line, Value.values() ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_DAY_TIME; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public boolean matches( World world, @Nullable BlockPos pos ) { + return (VALUE.matches( (int) (world.dayTime() / 24_000L) )) != INVERT; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DifficultyEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DifficultyEnvironment.java new file mode 100644 index 0000000..c7b4e70 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DifficultyEnvironment.java @@ -0,0 +1,40 @@ +package fathertoast.specialmobs.common.config.util.environment.time; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.CompareFloatEnvironment; +import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +/** + * Notes on regional difficulty: + * Maxes out over 63 days in the world and 150 days the in the chunk (effectively, time the chunk has been loaded). + * Peaks during the full moon and dramatically scaled by difficulty setting. + *

+ * Peaceful: 0 + * Easy: 0.75 to 1.5 (0.25 from world time, 0.375 from chunk time, and 0.125 from moon brightness) + * Normal: 1.5 to 4.0 (0.5 from world time, 1.5 from chunk time, and 0.5 from moon brightness) + * Hard: 2.25 to 6.75 (0.75 from world time, 3.0 from chunk time, and 0.75 from moon brightness) + */ +public class DifficultyEnvironment extends CompareFloatEnvironment { + + public DifficultyEnvironment( ComparisonOperator op, float value ) { super( op, value ); } + + public DifficultyEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The minimum value that can be given to the value. */ + protected float getMinValue() { return 0.0F; } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_DIFFICULTY; } + + /** @return Returns the actual value to compare, or Float.NaN if there isn't enough information. */ + @Override + public float getActual( World world, @Nullable BlockPos pos ) { + return pos == null ? Float.NaN : world.getCurrentDifficultyAt( pos ).getEffectiveDifficulty(); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/MoonBrightnessEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/MoonBrightnessEnvironment.java new file mode 100644 index 0000000..1e358a9 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/MoonBrightnessEnvironment.java @@ -0,0 +1,34 @@ +package fathertoast.specialmobs.common.config.util.environment.time; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.CompareFloatEnvironment; +import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.DimensionType; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class MoonBrightnessEnvironment extends CompareFloatEnvironment { + + public MoonBrightnessEnvironment( ComparisonOperator op, float value ) { super( op, value ); } + + public MoonBrightnessEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The minimum value that can be given to the value. */ + protected float getMinValue() { return 0.0F; } + + /** @return The maximum value that can be given to the value. */ + protected float getMaxValue() { return 1.0F; } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_MOON_BRIGHTNESS; } + + /** @return Returns the actual value to compare, or Float.NaN if there isn't enough information. */ + @Override + public float getActual( World world, @Nullable BlockPos pos ) { + return pos == null ? Float.NaN : DimensionType.MOON_BRIGHTNESS_PER_PHASE[world.dimensionType().moonPhase( world.dayTime() )]; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/MoonPhaseEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/MoonPhaseEnvironment.java new file mode 100644 index 0000000..4a92ddc --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/MoonPhaseEnvironment.java @@ -0,0 +1,38 @@ +package fathertoast.specialmobs.common.config.util.environment.time; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.EnumEnvironment; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class MoonPhaseEnvironment extends EnumEnvironment { + /** Values match up to the vanilla weather command. */ + public enum Value { + FULL( 0 ), WANING_GIBBOUS( 1 ), LAST_QUARTER( 2 ), WANING_CRESCENT( 3 ), + NEW( 4 ), WAXING_CRESCENT( 5 ), FIRST_QUARTER( 6 ), WAXING_GIBBOUS( 7 ); + + public final int INDEX; + + Value( int i ) { INDEX = i; } + } + + public MoonPhaseEnvironment( Value value ) { super( value ); } + + public MoonPhaseEnvironment( Value value, boolean invert ) { super( value, invert ); } + + public MoonPhaseEnvironment( AbstractConfigField field, String line ) { super( field, line, Value.values() ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_MOON_PHASE; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public boolean matches( World world, @Nullable BlockPos pos ) { + final int phase = world.dimensionType().moonPhase( world.dayTime() ); + return (VALUE.INDEX == phase) != INVERT; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/SpecialDifficultyEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/SpecialDifficultyEnvironment.java new file mode 100644 index 0000000..f33a7e7 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/SpecialDifficultyEnvironment.java @@ -0,0 +1,40 @@ +package fathertoast.specialmobs.common.config.util.environment.time; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.CompareFloatEnvironment; +import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +/** + * Notes on special multiplier difficulty: + * This is 0 while regional difficulty is <= 2 and this is 1 while regional difficulty is >= 4 (linearly scales between). + *

+ * In Peaceful and Easy, this is always 0. In Normal, this only maxes out at the absolute peak regional difficulty. + * In Hard, this starts out as 0.125 and reaches 1 during new moons with only ~50 days in the area. + */ +public class SpecialDifficultyEnvironment extends CompareFloatEnvironment { + + public SpecialDifficultyEnvironment( ComparisonOperator op, float value ) { super( op, value ); } + + public SpecialDifficultyEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The minimum value that can be given to the value. */ + protected float getMinValue() { return 0.0F; } + + /** @return The maximum value that can be given to the value. */ + protected float getMaxValue() { return 1.0F; } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_SPECIAL_DIFFICULTY; } + + /** @return Returns the actual value to compare, or Float.NaN if there isn't enough information. */ + @Override + public float getActual( World world, @Nullable BlockPos pos ) { + return pos == null ? Float.NaN : world.getCurrentDifficultyAt( pos ).getSpecialMultiplier(); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/TimeFromMidnightEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/TimeFromMidnightEnvironment.java new file mode 100644 index 0000000..95053d2 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/TimeFromMidnightEnvironment.java @@ -0,0 +1,37 @@ +package fathertoast.specialmobs.common.config.util.environment.time; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.CompareIntEnvironment; +import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class TimeFromMidnightEnvironment extends CompareIntEnvironment { + + public TimeFromMidnightEnvironment( ComparisonOperator op, int value ) { super( op, value ); } + + public TimeFromMidnightEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_TIME_FROM_MIDNIGHT; } + + /** @return The minimum value that can be given to the value. */ + @Override + protected int getMinValue() { return 0; } + + /** @return The maximum value that can be given to the value. */ + @Override + protected int getMaxValue() { return 12_000; } + + /** @return Returns the actual value to compare, or null if there isn't enough information. */ + @Override + public Integer getActual( World world, @Nullable BlockPos pos ) { + int dayTime = (int) (world.dayTime() / 24_000L); + if( dayTime < 18_000 ) dayTime += 24_000; + return dayTime - 18_000; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/WeatherEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/WeatherEnvironment.java new file mode 100644 index 0000000..5d16e1f --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/WeatherEnvironment.java @@ -0,0 +1,32 @@ +package fathertoast.specialmobs.common.config.util.environment.time; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.EnumEnvironment; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class WeatherEnvironment extends EnumEnvironment { + /** Values match up to the vanilla weather command. */ + public enum Value { CLEAR, RAIN, THUNDER } + + public WeatherEnvironment( Value value ) { super( value ); } + + public WeatherEnvironment( Value value, boolean invert ) { super( value, invert ); } + + public WeatherEnvironment( AbstractConfigField field, String line ) { super( field, line, Value.values() ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_BIOME_CATEGORY; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public boolean matches( World world, @Nullable BlockPos pos ) { + if( world.getLevelData().isThundering() ) return (VALUE == Value.CLEAR) == INVERT; // Thunder implies rain + if( world.getLevelData().isRaining() ) return (VALUE == Value.RAIN) != INVERT; + return (VALUE == Value.CLEAR) != INVERT; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/WorldTimeEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/WorldTimeEnvironment.java new file mode 100644 index 0000000..799faff --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/WorldTimeEnvironment.java @@ -0,0 +1,29 @@ +package fathertoast.specialmobs.common.config.util.environment.time; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.CompareLongEnvironment; +import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class WorldTimeEnvironment extends CompareLongEnvironment { + + public WorldTimeEnvironment( ComparisonOperator op, long value ) { super( op, value ); } + + public WorldTimeEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The minimum value that can be given to the value. */ + @Override + protected long getMinValue() { return 0L; } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_MOON_BRIGHTNESS; } + + /** @return Returns the actual value to compare, or null if there isn't enough information. */ + @Override + public Long getActual( World world, @Nullable BlockPos pos ) { return world.dayTime(); } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/package-info.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/package-info.java new file mode 100644 index 0000000..6782ba4 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.config.util.environment.time; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/package-info.java b/src/main/java/fathertoast/specialmobs/common/config/util/package-info.java new file mode 100644 index 0000000..aa9f9da --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.config.util; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobReplacer.java b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobReplacer.java index 359c630..b1152e7 100644 --- a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobReplacer.java +++ b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobReplacer.java @@ -4,7 +4,6 @@ import fathertoast.specialmobs.common.bestiary.MobFamily; import fathertoast.specialmobs.common.config.Config; import fathertoast.specialmobs.common.entity.MobHelper; import fathertoast.specialmobs.common.util.References; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; import net.minecraft.nbt.CompoundNBT; @@ -18,12 +17,9 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import javax.annotation.Nullable; -import javax.annotation.ParametersAreNonnullByDefault; import java.util.ArrayDeque; import java.util.Deque; -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @Mod.EventBusSubscriber( modid = SpecialMobs.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE ) public final class SpecialMobReplacer { /** List of data for mobs needing replacement. */ diff --git a/src/main/java/fathertoast/specialmobs/common/core/package-info.java b/src/main/java/fathertoast/specialmobs/common/core/package-info.java new file mode 100644 index 0000000..db2795a --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/core/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.core; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/core/register/package-info.java b/src/main/java/fathertoast/specialmobs/common/core/register/package-info.java new file mode 100644 index 0000000..e96eecd --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/core/register/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.core.register; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file From 999d9b3edf1212a173df85660d8484f07b857354 Mon Sep 17 00:00:00 2001 From: FatherToast Date: Sun, 17 Jul 2022 11:44:56 -0500 Subject: [PATCH 05/16] last few extra conditions --- .../config/field/EnvironmentListField.java | 15 +++--- .../position/PositionEnvironment.java | 47 +++++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/position/PositionEnvironment.java diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java b/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java index 4d4bde8..a0acfcd 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java @@ -8,10 +8,7 @@ import fathertoast.specialmobs.common.config.util.environment.biome.*; import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionPropertyEnvironment; import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionTypeEnvironment; import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionTypeGroupEnvironment; -import fathertoast.specialmobs.common.config.util.environment.position.StructureEnvironment; -import fathertoast.specialmobs.common.config.util.environment.position.StructureGroupEnvironment; -import fathertoast.specialmobs.common.config.util.environment.position.YEnvironment; -import fathertoast.specialmobs.common.config.util.environment.position.YFromSeaEnvironment; +import fathertoast.specialmobs.common.config.util.environment.position.*; import fathertoast.specialmobs.common.config.util.environment.time.*; import fathertoast.specialmobs.common.core.SpecialMobs; import net.minecraft.util.math.BlockPos; @@ -42,10 +39,11 @@ public class EnvironmentListField extends GenericField { public static final String ENV_TEMPERATURE = "temp"; public static final String ENV_BIOME_CATEGORY = "biome_category"; public static final String ENV_BIOME = "biome"; - // Position-based TODO can_see_sky + // Position-based public static final String ENV_STRUCTURE = "structure"; public static final String ENV_Y = "y"; public static final String ENV_Y_FROM_SEA = "y_from_sea"; + public static final String ENV_POSITION = "position"; // Time-based public static final String ENV_DIFFICULTY = "difficulty"; public static final String ENV_SPECIAL_DIFFICULTY = "special_difficulty"; @@ -101,6 +99,9 @@ public class EnvironmentListField extends GenericField { 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( " \"" + 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." ); // Time-based comment.add( " \"" + ENV_DIFFICULTY + " op value\":" ); comment.add( " The regional difficulty (0 to 6.75). This is based on many factors such as difficulty setting, moon brightness, chunk inhabited time, and world time." ); @@ -239,6 +240,8 @@ public class EnvironmentListField extends GenericField { return new YEnvironment( this, value ); case ENV_Y_FROM_SEA: return new YFromSeaEnvironment( this, value ); + case ENV_POSITION: + return new PositionEnvironment( this, value ); // Time-based case ENV_DIFFICULTY: return new DifficultyEnvironment( this, value ); @@ -265,7 +268,7 @@ public class EnvironmentListField extends GenericField { // Biome-based ENV_RAINFALL, ENV_BIOME_TEMPERATURE, ENV_TEMPERATURE, ENV_BIOME_CATEGORY, ENV_BIOME, // Position-based - ENV_STRUCTURE, ENV_Y, ENV_Y_FROM_SEA, + 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 diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/PositionEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/PositionEnvironment.java new file mode 100644 index 0000000..71c3190 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/PositionEnvironment.java @@ -0,0 +1,47 @@ +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.EnumEnvironment; +import net.minecraft.tags.FluidTags; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; + +import javax.annotation.Nullable; +import java.util.function.BiFunction; + +public class PositionEnvironment extends EnumEnvironment { + + public enum Value { + CAN_SEE_SKY( ( world, pos ) -> pos != null && world.canSeeSky( pos ) ), + 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_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() ), + HAS_REDSTONE_POWER( ( world, pos ) -> pos != null && world.getDirectSignalTo( pos ) > 0 ); + + private final BiFunction SUPPLIER; + + Value( BiFunction supplier ) { SUPPLIER = supplier; } + + 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() ); } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_POSITION; } + + /** @return Returns true if this environment matches the provided environment. */ + @Override + public boolean matches( World world, @Nullable BlockPos pos ) { return VALUE.of( world, pos ) != INVERT; } +} \ No newline at end of file From 74a106ba6aa1c78f7c25107a45baf038d77de28f Mon Sep 17 00:00:00 2001 From: FatherToast Date: Sun, 17 Jul 2022 22:55:12 -0500 Subject: [PATCH 06/16] Environment-sensitive special chance! --- .../specialmobs/common/config/Config.java | 1 + .../specialmobs/common/config/MainConfig.java | 14 -- .../common/config/ReadMeConfig.java | 36 ++++ .../common/config/family/FamilyConfig.java | 19 +- .../common/config/field/DoubleField.java | 89 ++++++++ .../config/field/EnvironmentListField.java | 19 +- .../common/config/file/ToastConfigSpec.java | 33 +++ .../common/config/file/ToastTomlWriter.java | 3 +- .../common/config/species/SpeciesConfig.java | 2 - .../common/config/util/EnvironmentEntry.java | 193 +++++++++++++++++- .../common/config/util/WeightedList.java | 19 +- .../util/environment/ComparisonOperator.java | 70 +++---- .../DynamicRegistryEnvironment.java | 2 - .../DynamicRegistryGroupEnvironment.java | 2 - .../util/environment/EnumEnvironment.java | 2 - .../util/environment/RegistryEnvironment.java | 2 - .../environment/RegistryGroupEnvironment.java | 10 +- .../util/environment/biome/BiomeCategory.java | 31 +++ .../biome/BiomeCategoryEnvironment.java | 32 +-- .../environment/biome/BiomeEnvironment.java | 2 - .../biome/BiomeGroupEnvironment.java | 5 +- .../biome/BiomeTemperatureEnvironment.java | 17 +- .../biome/TemperatureEnvironment.java | 15 +- .../DimensionPropertyEnvironment.java | 2 - .../dimension/DimensionTypeEnvironment.java | 2 - .../DimensionTypeGroupEnvironment.java | 5 +- .../position/PositionEnvironment.java | 4 +- .../position/StructureEnvironment.java | 2 - .../position/StructureGroupEnvironment.java | 5 +- .../time/ChunkTimeEnvironment.java | 33 +++ .../environment/time/DayTimeEnvironment.java | 2 - .../time/DifficultyEnvironment.java | 3 + .../time/MoonBrightnessEnvironment.java | 2 + .../time/MoonPhaseEnvironment.java | 2 - .../time/SpecialDifficultyEnvironment.java | 2 + .../environment/time/WeatherEnvironment.java | 4 +- .../time/WorldTimeEnvironment.java | 2 +- .../common/core/SpecialMobReplacer.java | 2 +- 38 files changed, 520 insertions(+), 170 deletions(-) create mode 100644 src/main/java/fathertoast/specialmobs/common/config/ReadMeConfig.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategory.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/time/ChunkTimeEnvironment.java diff --git a/src/main/java/fathertoast/specialmobs/common/config/Config.java b/src/main/java/fathertoast/specialmobs/common/config/Config.java index 523c913..ddb80f3 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/Config.java +++ b/src/main/java/fathertoast/specialmobs/common/config/Config.java @@ -24,6 +24,7 @@ public class Config { /** Performs initial loading of all configs in this mod. */ public static void initialize() { ToastConfigSpec.freezeFileWatcher = true; + ReadMeConfig.makeReadMe( CONFIG_DIR ); MAIN.SPEC.initialize(); MobFamily.initBestiary(); ToastConfigSpec.freezeFileWatcher = false; diff --git a/src/main/java/fathertoast/specialmobs/common/config/MainConfig.java b/src/main/java/fathertoast/specialmobs/common/config/MainConfig.java index e814bf1..7ce1524 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/MainConfig.java +++ b/src/main/java/fathertoast/specialmobs/common/config/MainConfig.java @@ -15,20 +15,6 @@ public class MainConfig extends Config.AbstractConfig { super( dir, fileName, "This config contains options that apply to the mod as a whole, including some master settings", "toggles for convenience." ); - SPEC.newLine(); - SPEC.comment( - "Terminology used in Special Mobs configs:", - " * Mob - An entity that is 'alive', short for \"Mobile\" or MobEntity.", - " * Family - The group of mobs based on (but not including) a particular vanilla mob; e.g., Creepers.", - " * Species - A specific type of mob within a family; e.g., Fire Creepers or vanilla-replacement Creepers.", - " * Vanilla Replacement - The one species within a family that is intended to be a replica of the base vanilla mob.", - " * Special Variant - Any species that is not the family's vanilla replacement. Includes species that are", - " replicas of 'vanilla special variants'; i.e. Husks and Strays.", - " * Mob Replacer - The tool that watches vanilla mob spawns and cancels them to spawn this mod's entities." ); - SPEC.newLine(); - SPEC.describeAttributeList(); - SPEC.newLine(); - SPEC.describeRegistryEntryList(); GENERAL = new General( SPEC ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/ReadMeConfig.java b/src/main/java/fathertoast/specialmobs/common/config/ReadMeConfig.java new file mode 100644 index 0000000..a9a40ab --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/ReadMeConfig.java @@ -0,0 +1,36 @@ +package fathertoast.specialmobs.common.config; + +import fathertoast.specialmobs.common.config.field.BooleanField; + +import java.io.File; + +public class ReadMeConfig extends Config.AbstractConfig { + @SuppressWarnings( "SameParameterValue" ) + static void makeReadMe( File dir ) { new ReadMeConfig( dir ).SPEC.initialize(); } + + /** Builds the config spec that should be used for this config. */ + private ReadMeConfig( File dir ) { + super( dir, "__README__", "This file contains helpful information about how to use the config files in this mod." ); + SPEC.newLine( 2 ); + SPEC.comment( + "Terminology used in Special Mobs configs:", + " * Mob - An entity that is 'alive', short for \"Mobile\" or MobEntity.", + " * Family - The group of mobs based on (but not including) a particular vanilla mob; e.g., Creepers.", + " * Species - A specific type of mob within a family; e.g., Fire Creepers or vanilla-replacement Creepers.", + " * Vanilla Replacement - The one species within a family that is intended to be a replica of the base vanilla mob.", + " * Special Variant - Any species that is not the family's vanilla replacement. Includes species that are", + " replicas of 'vanilla special variants'; i.e. Husks and Strays.", + " * Mob Replacer - The tool that watches vanilla mob spawns and cancels them to spawn this mod's entities." ); + SPEC.newLine( 2 ); + SPEC.describeAttributeList(); + SPEC.newLine( 2 ); + SPEC.describeRegistryEntryList(); + SPEC.newLine( 2 ); + SPEC.describeEnvironmentListPart1of2(); + SPEC.newLine(); + SPEC.describeEnvironmentListPart2of2(); + + SPEC.newLine( 4 ); + SPEC.define( new BooleanField( "secret_mode", false, (String[]) null ) ); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/family/FamilyConfig.java b/src/main/java/fathertoast/specialmobs/common/config/family/FamilyConfig.java index 95939bd..1d203a9 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/family/FamilyConfig.java +++ b/src/main/java/fathertoast/specialmobs/common/config/family/FamilyConfig.java @@ -4,10 +4,13 @@ import fathertoast.specialmobs.common.bestiary.MobFamily; import fathertoast.specialmobs.common.config.Config; import fathertoast.specialmobs.common.config.field.BooleanField; import fathertoast.specialmobs.common.config.field.DoubleField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; import fathertoast.specialmobs.common.config.field.IntField; import fathertoast.specialmobs.common.config.file.ToastConfigSpec; import fathertoast.specialmobs.common.config.file.TomlHelper; import fathertoast.specialmobs.common.config.util.ConfigUtil; +import fathertoast.specialmobs.common.config.util.EnvironmentEntry; +import fathertoast.specialmobs.common.config.util.EnvironmentList; import java.io.File; import java.util.List; @@ -51,7 +54,7 @@ public class FamilyConfig extends Config.AbstractConfig { public final DoubleField familyRandomScaling; - public final DoubleField specialVariantChance; + public final DoubleField.EnvironmentSensitive specialVariantChance; public final IntField[] specialVariantWeights; @@ -72,9 +75,17 @@ public class FamilyConfig extends Config.AbstractConfig { SPEC.newLine(); - specialVariantChance = SPEC.define( new DoubleField( "special_variant_chance", variantChance, DoubleField.Range.PERCENT, - "The chance for " + family.configName + " to spawn as special variants." ) ); - // TODO special variant chance exceptions + specialVariantChance = new DoubleField.EnvironmentSensitive( + SPEC.define( new DoubleField( "special_variant_chance.base", variantChance, DoubleField.Range.PERCENT, + "The chance for " + family.configName + " to spawn as special variants." ) ), + SPEC.define( new EnvironmentListField( "special_variant_chance.exceptions", new EnvironmentList( + EnvironmentEntry.builder( (float) variantChance * 0.5F ).beforeDays( 5 ).build(), // Also skips first night's full moon + EnvironmentEntry.builder( (float) variantChance * 2.0F ).atMaxMoonLight().aboveDifficulty( 0.5F ).build(), + EnvironmentEntry.builder( (float) variantChance * 1.5F ).atMaxMoonLight().build(), + EnvironmentEntry.builder( (float) variantChance * 1.5F ).aboveDifficulty( 0.5F ).build() ) + .setRange( DoubleField.Range.PERCENT ), + "The chance for " + family.configName + " to spawn as special variants when specific environmental conditions are met." ) ) + ); SPEC.newLine(); diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/DoubleField.java b/src/main/java/fathertoast/specialmobs/common/config/field/DoubleField.java index a3ecccd..c7a13d0 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/DoubleField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/DoubleField.java @@ -2,8 +2,12 @@ package fathertoast.specialmobs.common.config.field; import fathertoast.specialmobs.common.config.file.TomlHelper; import fathertoast.specialmobs.common.core.SpecialMobs; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Random; @@ -156,4 +160,89 @@ public class DoubleField extends AbstractConfigField { return getMin(); } } + + /** + * Represents a double field and an environment exception list, combined into one. + * This has convenience methods for returning the value that should be used based on the environment. + */ + public static class EnvironmentSensitive { + /** The base value. */ + private final DoubleField BASE; + /** The environment exceptions list. */ + private final EnvironmentListField EXCEPTIONS; + + /** Links two fields together as base and exceptions. */ + public EnvironmentSensitive( DoubleField base, EnvironmentListField exceptions ) { + BASE = base; + EXCEPTIONS = exceptions; + } + + /** @return Returns the config field's value. */ + public double get( World world, @Nullable BlockPos pos ) { return EXCEPTIONS.getOrElse( world, pos, BASE ); } + + /** @return Treats the config field's value as a percent chance (from 0 to 1) and returns the result of a single roll. */ + public boolean rollChance( Random random, World world, @Nullable BlockPos pos ) { return random.nextDouble() < get( world, pos ); } + } + + /** + * Represents an environment sensitive list of weighted values. Unlike the normal weighted list, this is just a simple + * wrapper class, and its weights are doubles. + * It sacrifices automation for flexibility, largely to help with the craziness of environment list fields. + */ + public static class EnvironmentSensitiveWeightedList { + + private final List> UNDERLYING_LIST; + + /** Links an array of values to two arrays of fields as base weights and exceptions. */ + public EnvironmentSensitiveWeightedList( T[] values, DoubleField[] baseWeights, EnvironmentListField[] weightExceptions ) { + if( values.length != baseWeights.length || values.length != weightExceptions.length ) + throw new IllegalArgumentException( "All arrays must be equal length!" ); + + final ArrayList> list = new ArrayList<>(); + for( int i = 0; i < values.length; i++ ) { + list.add( new Entry<>( values[i], new EnvironmentSensitive( baseWeights[i], weightExceptions[i] ) ) ); + + // Do a bit of error checking; allows us to ignore the possibility of negative weights + if( baseWeights[i].valueMin < 0.0 || weightExceptions[i].valueDefault.getMinValue() < 0.0 ) { + throw new IllegalArgumentException( "Weight is not allowed to be negative! See " + + baseWeights[i].getKey() + " and/or " + weightExceptions[i].getKey() ); + } + } + list.trimToSize(); + UNDERLYING_LIST = Collections.unmodifiableList( list ); + } + + /** @return Returns a random item from this weighted list. Null if none of the items have a positive weight. */ + @Nullable + public T next( Random random, World world, @Nullable BlockPos pos ) { + // Due to the 'nebulous' nature of environment-based weights, we must recalculate weights for EVERY call + final double[] weights = new double[UNDERLYING_LIST.size()]; + double targetWeight = 0.0; + for( int i = 0; i < weights.length; i++ ) { + targetWeight += weights[i] = UNDERLYING_LIST.get( i ).WEIGHT.get( world, pos ); + } + if( targetWeight <= 0.0 ) return null; + + // Now we unravel the target weight to a random point + targetWeight *= random.nextDouble(); + for( int i = 0; i < weights.length; i++ ) { + targetWeight -= weights[i]; + if( targetWeight < 0.0 ) return UNDERLYING_LIST.get( i ).VALUE; + } + + SpecialMobs.LOG.error( "Environment-sensitive weight list was unable to return a value when it should have! " + + "This is probably due to error in floating point calculations, perhaps try changing the scale of weights." ); + return null; + } + + private static class Entry { + final T VALUE; + final DoubleField.EnvironmentSensitive WEIGHT; + + Entry( T value, DoubleField.EnvironmentSensitive weight ) { + VALUE = value; + WEIGHT = weight; + } + } + } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java b/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java index a0acfcd..3481ea3 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java @@ -17,6 +17,7 @@ import net.minecraft.world.World; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; +import java.util.Locale; /** * Represents a config field with an environment list value. @@ -53,6 +54,7 @@ public class EnvironmentListField extends GenericField { public static final String ENV_DAY_TIME = "day_time"; public static final String ENV_TIME_FROM_MIDNIGHT = "time_from_midnight"; public static final String ENV_WORLD_TIME = "world_time"; + public static final String ENV_CHUNK_TIME = "chunk_time"; /** * Provides a description of how to use environment lists. Recommended to put at the top of any file using environment lists. @@ -78,7 +80,7 @@ public class EnvironmentListField extends GenericField { comment.add( " \"" + ENV_DIMENSION_PROPERTY + " (!)property\":" ); comment.add( " Valid property values: " + TomlHelper.literalList( (Object[]) DimensionPropertyEnvironment.Value.values() ) ); comment.add( " Dimension properties are the true/false values available to dimension types in data packs." ); - comment.add( " See the wiki for more info: [https://minecraft.fandom.com/wiki/Custom_dimension#Syntax]." ); + comment.add( " See the wiki for more info: [https://minecraft.fandom.com/wiki/Custom_dimension#Syntax]." ); comment.add( " \"" + ENV_DIMENSION_TYPE + " (!)namespace:dimension_type_name\":" ); comment.add( " The world's dimension type. In vanilla, these are only \"minecraft:overworld\", \"minecraft:the_nether\", or \"minecraft:the_end\"." ); // Biome-based @@ -89,7 +91,7 @@ public class EnvironmentListField extends GenericField { comment.add( " \"" + ENV_TEMPERATURE + " op value\" or \"" + ENV_TEMPERATURE + " (!)" + TemperatureEnvironment.FREEZING + "\":" ); comment.add( " Height-adjusted temperature. For reference, freezing is < 0.15 and hot is generally considered > 0.95." ); comment.add( " \"" + ENV_BIOME_CATEGORY + " (!)category\":" ); - comment.add( " Valid category values: " + TomlHelper.literalList( (Object[]) BiomeCategoryEnvironment.Value.values() ) ); + comment.add( " Valid category values: " + TomlHelper.literalList( (Object[]) BiomeCategory.values() ) ); comment.add( " \"" + ENV_BIOME + " (!)namespace:biome_name\":" ); comment.add( " The biome. See the wiki for vanilla biome names (resource locations) [https://minecraft.fandom.com/wiki/Biome#Biome_IDs]." ); // Position-based @@ -98,7 +100,7 @@ public class EnvironmentListField extends GenericField { comment.add( " \"" + ENV_Y + " op value\":" ); comment.add( " The y-value. For reference, sea level is normally 63 and lava level is normally 10." );//TODO change lava level to -54 for MC 1.18 comment.add( " \"" + ENV_Y_FROM_SEA + " op value\":" ); - comment.add( " The y-value from sea level. Expect the only air <= 0 to be in caves (which may still have direct view of the sky)." ); + comment.add( " The y-value from sea level. Expect the only air <= 0 to be in caves/ravines (which may still have direct view of the sky)." ); comment.add( " \"" + ENV_POSITION + " (!)state\":" ); comment.add( " Valid state values: " + TomlHelper.literalList( (Object[]) PositionEnvironment.Value.values() ) ); comment.add( " Miscellaneous conditions that generally do what you expect. For reference, 'near' a village is ~3 chunks, and redstone checks weak power." ); @@ -123,8 +125,11 @@ public class EnvironmentListField extends GenericField { comment.add( " The absolute time in ticks away from midnight. Value must be 0 to 12000." ); comment.add( " \"" + ENV_MOON_PHASE + " (!)phase\":" ); comment.add( " Valid phase values: " + TomlHelper.literalList( (Object[]) MoonPhaseEnvironment.Value.values() ) ); + comment.add( " For reference, the first day in a new world is always a full moon." ); comment.add( " \"" + ENV_WORLD_TIME + " op value\":" ); comment.add( " The total time the world has existed, in ticks. For reference, each day cycle is 24000 ticks and each lunar cycle is 192000 ticks." ); + comment.add( " \"" + ENV_CHUNK_TIME + " op value\":" ); + comment.add( " The total time the chunk has been loaded, in ticks. For reference, each day cycle is 24000 ticks and each lunar cycle is 192000 ticks." ); return comment; } @@ -167,7 +172,7 @@ public class EnvironmentListField extends GenericField { final List conditions = new ArrayList<>(); if( args.length > 1 ) { - final String[] condArgs = args[1].trim().split( "&" ); + final String[] condArgs = args[1].split( "&" ); for( String condArg : condArgs ) { conditions.add( parseCondition( condArg.trim(), line ) ); } @@ -216,7 +221,7 @@ public class EnvironmentListField extends GenericField { if( args.length < 2 ) value = ""; else value = args[1].trim(); - switch( args[0] ) { + switch( args[0].toLowerCase( Locale.ROOT ) ) { // Dimension-based case ENV_DIMENSION_PROPERTY: return new DimensionPropertyEnvironment( this, value ); @@ -259,6 +264,8 @@ public class EnvironmentListField extends GenericField { return new TimeFromMidnightEnvironment( this, value ); case ENV_WORLD_TIME: return new WorldTimeEnvironment( this, value ); + case ENV_CHUNK_TIME: + return new ChunkTimeEnvironment( this, value ); } // The environment name was not recognized; try to provide some good feedback because this field is complicated @@ -271,7 +278,7 @@ public class EnvironmentListField extends GenericField { ENV_STRUCTURE, ENV_Y, ENV_Y_FROM_SEA, ENV_POSITION, // Time-based ENV_DIFFICULTY, ENV_SPECIAL_DIFFICULTY, ENV_WEATHER, ENV_MOON_BRIGHTNESS, ENV_MOON_PHASE, ENV_DAY_TIME, - ENV_TIME_FROM_MIDNIGHT, ENV_WORLD_TIME + ENV_TIME_FROM_MIDNIGHT, ENV_WORLD_TIME, ENV_CHUNK_TIME }; final AbstractEnvironment fallback = new YEnvironment( ComparisonOperator.LESS_THAN, 0 ); SpecialMobs.LOG.warn( "Invalid environment '{}' for {} \"{}\"! Falling back to \"{}\". Environment name must be in the set [ {} ]. Invalid environment: {}", diff --git a/src/main/java/fathertoast/specialmobs/common/config/file/ToastConfigSpec.java b/src/main/java/fathertoast/specialmobs/common/config/file/ToastConfigSpec.java index dfd5ebc..0e313fe 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/file/ToastConfigSpec.java +++ b/src/main/java/fathertoast/specialmobs/common/config/file/ToastConfigSpec.java @@ -227,6 +227,30 @@ public class ToastConfigSpec { } } + /** Represents an appendix header comment. */ + private static class AppendixHeader extends Format { + /** The appendix comment. */ + private final List COMMENT; + + /** Create a new header action that will insert the opening file comment. */ + private AppendixHeader( List comment ) { + COMMENT = comment; + } + + /** Called when the config is saved. */ + @Override + public void write( ToastTomlWriter writer, CharacterOutput output ) { + writer.decreaseIndentLevel(); + writer.writeNewLine( output ); + writer.writeNewLine( output ); + + writer.writeComment( "Appendix:", output ); + writer.writeComment( COMMENT, output ); + + writer.increaseIndentLevel(); + } + } + /** Represents a category comment. */ private static class Category extends Format { /** The category comment. */ @@ -355,6 +379,9 @@ public class ToastConfigSpec { /** @param comment The file comment to insert. */ public void header( List comment ) { ACTIONS.add( new Header( this, comment ) ); } + /** @param comment The appendix comment to insert. */ + public void appendixHeader( String... comment ) { ACTIONS.add( new AppendixHeader( TomlHelper.newComment( comment ) ) ); } + /** Inserts a detailed description of how to use the registry entry list field. */ public void describeRegistryEntryList() { ACTIONS.add( new Comment( RegistryEntryListField.verboseDescription() ) ); } @@ -367,6 +394,12 @@ public class ToastConfigSpec { /** Inserts a detailed description of how to use the block list field. */ public void describeBlockList() { ACTIONS.add( new Comment( BlockListField.verboseDescription() ) ); } + /** Inserts the first part of a detailed description of how to use the environment list field. Should go with the other field descriptions. */ + public void describeEnvironmentListPart1of2() { ACTIONS.add( new Comment( EnvironmentListField.verboseDescription() ) ); } + + /** Inserts the second and last part of a detailed description of how to use the environment list field. Should go at the bottom of the file. */ + public void describeEnvironmentListPart2of2() { ACTIONS.add( new Comment( EnvironmentListField.environmentDescriptions() ) ); } + /** * @param name The category name. * @param comment The category comment to insert. diff --git a/src/main/java/fathertoast/specialmobs/common/config/file/ToastTomlWriter.java b/src/main/java/fathertoast/specialmobs/common/config/file/ToastTomlWriter.java index 4c3ab8a..fb9e442 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/file/ToastTomlWriter.java +++ b/src/main/java/fathertoast/specialmobs/common/config/file/ToastTomlWriter.java @@ -5,6 +5,7 @@ import com.electronwill.nightconfig.core.io.*; import fathertoast.specialmobs.common.config.field.AbstractConfigField; import fathertoast.specialmobs.common.core.SpecialMobs; +import javax.annotation.Nullable; import java.io.Writer; import java.util.Iterator; import java.util.List; @@ -85,7 +86,7 @@ public class ToastTomlWriter implements ConfigWriter { } /** Writes a literal array of single-line strings. */ - public void writeStringArray( List list, CharacterOutput output ) { + public void writeStringArray( @Nullable List list, CharacterOutput output ) { if( list == null || list.isEmpty() ) { writeLine( "[]", output ); return; diff --git a/src/main/java/fathertoast/specialmobs/common/config/species/SpeciesConfig.java b/src/main/java/fathertoast/specialmobs/common/config/species/SpeciesConfig.java index 1498cd0..108b22b 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/species/SpeciesConfig.java +++ b/src/main/java/fathertoast/specialmobs/common/config/species/SpeciesConfig.java @@ -38,8 +38,6 @@ public class SpeciesConfig extends Config.AbstractConfig { super( FamilyConfig.dir( species.family ), fileName( species ), String.format( "This config contains options that apply only to the %s %s species.", variantName( species ), ConfigUtil.camelCaseToLowerSpace( species.family.name ) ) ); - SPEC.newLine(); - SPEC.comment( "See the main mod config for common terms and how to use special field types like Attribute List or Registry List." ); speciesName = variantName( species ) + " " + species.family.configName; diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java index 6ea47e4..4b66e24 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java @@ -1,16 +1,20 @@ package fathertoast.specialmobs.common.config.util; import fathertoast.specialmobs.common.config.util.environment.*; -import fathertoast.specialmobs.common.config.util.environment.biome.BiomeEnvironment; +import fathertoast.specialmobs.common.config.util.environment.biome.*; +import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionPropertyEnvironment; import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionTypeEnvironment; +import fathertoast.specialmobs.common.config.util.environment.dimension.DimensionTypeGroupEnvironment; +import fathertoast.specialmobs.common.config.util.environment.position.PositionEnvironment; import fathertoast.specialmobs.common.config.util.environment.position.StructureEnvironment; import fathertoast.specialmobs.common.config.util.environment.position.YEnvironment; import fathertoast.specialmobs.common.config.util.environment.position.YFromSeaEnvironment; +import fathertoast.specialmobs.common.config.util.environment.time.*; import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.DimensionType; import net.minecraft.world.World; -import net.minecraft.world.biome.Biome; import net.minecraft.world.gen.feature.structure.Structure; import javax.annotation.Nullable; @@ -64,10 +68,20 @@ public class EnvironmentEntry { return str.toString(); } + + // Builder Implementation + + /** Creates a new entry builder. The value is rounded to 2 decimal place precision. */ + public static Builder builder( float value ) { return new Builder( Math.round( value * 100.0 ) / 100.0 ); } + + /** Creates a new entry builder. */ public static Builder builder( double value ) { return new Builder( value ); } /** - * Builder class used to simplify creation of environment entries, with shortcuts for the most commonly used environments. + * Builder class used to simplify creation of environment entries for default configs, + * with shortcuts for the most commonly used environments. + *

+ * Keep in mind that ALL conditions in an entry must be satisfied for it to be chosen. */ public static class Builder { private final double VALUE; @@ -85,28 +99,187 @@ public class EnvironmentEntry { // Dimension-based - public Builder inDimensionType( RegistryKey dimType ) { return in( new DimensionTypeEnvironment( dimType ) ); } + /** Check if the dimension type causes water to instantly vaporize and has faster lava flow. */ + public Builder inUltraWarmDimension() { return inDimensionWithProperty( DimensionPropertyEnvironment.Value.ULTRAWARM, false ); } + + /** Check if the dimension type causes water to instantly vaporize and has faster lava flow. */ + public Builder notInUltraWarmDimension() { return inDimensionWithProperty( DimensionPropertyEnvironment.Value.ULTRAWARM, true ); } + + /** Check if the dimension type allows clocks, compasses, and beds to work. */ + public Builder inNaturalDimension() { return inDimensionWithProperty( DimensionPropertyEnvironment.Value.NATURAL, false ); } + + /** Check if the dimension type allows clocks, compasses, and beds to work. */ + public Builder notInNaturalDimension() { return inDimensionWithProperty( DimensionPropertyEnvironment.Value.NATURAL, true ); } + + private Builder inDimensionWithProperty( DimensionPropertyEnvironment.Value property, boolean invert ) { + return in( new DimensionPropertyEnvironment( property, invert ) ); + } + + public Builder inOverworld() { return inDimensionType( DimensionType.OVERWORLD_LOCATION, false ); } + + public Builder notInOverworld() { return inDimensionType( DimensionType.OVERWORLD_LOCATION, true ); } + + public Builder inNether() { return inDimensionType( DimensionType.NETHER_LOCATION, false ); } + + public Builder notInNether() { return inDimensionType( DimensionType.NETHER_LOCATION, true ); } + + public Builder inTheEnd() { return inDimensionType( DimensionType.END_LOCATION, false ); } + + public Builder notInTheEnd() { return inDimensionType( DimensionType.END_LOCATION, true ); } + + private Builder inDimensionType( RegistryKey dimType, boolean invert ) { return in( new DimensionTypeEnvironment( dimType, invert ) ); } + + /** Check if the dimension type is vanilla (registered with the "minecraft" namespace). */ + public Builder inVanillaDimension() { return in( new DimensionTypeGroupEnvironment( new ResourceLocation( "" ), false ) ); } + + /** Check if the dimension type is vanilla (registered with the "minecraft" namespace). */ + public Builder notInVanillaDimension() { return in( new DimensionTypeGroupEnvironment( new ResourceLocation( "" ), true ) ); } // Biome-based - public Builder inBiome( RegistryKey biome ) { return in( new BiomeEnvironment( biome ) ); } + /** Check if the biome has rain disabled. */ + public Builder inDryBiome() { return inAvgRainfall( ComparisonOperator.EQUAL_TO, 0.0F ); } + + /** Check if the biome has rain disabled. */ + public Builder notInDryBiome() { return inAvgRainfall( ComparisonOperator.EQUAL_TO.invert(), 0.0F ); } + + /** Check if the biome's humidity hinders fire spread. */ + public Builder inHumidBiome() { return inAvgRainfall( ComparisonOperator.GREATER_THAN, 0.85F ); } + + /** Check if the biome's humidity hinders fire spread. */ + public Builder notInHumidBiome() { return inAvgRainfall( ComparisonOperator.GREATER_THAN.invert(), 0.85F ); } + + private Builder inAvgRainfall( ComparisonOperator op, float value ) { return in( new RainfallEnvironment( op, value ) ); } + + /** Check if the temperature is freezing. */ + public Builder isFreezing() { return in( new TemperatureEnvironment( true ) ); } + + /** Check if the temperature is freezing. */ + public Builder isNotFreezing() { return in( new TemperatureEnvironment( false ) ); } + + /** Check if the temperature is warm (disables snow golem trails). */ + public Builder isWarm() { return isTemperature( ComparisonOperator.GREATER_OR_EQUAL, 0.8F ); } + + /** Check if the temperature is warm (disables snow golem trails). */ + public Builder isNotWarm() { return isTemperature( ComparisonOperator.GREATER_OR_EQUAL.invert(), 0.8F ); } + + /** Check if the temperature is hot (causes snow golems to die). */ + public Builder isHot() { return isTemperature( ComparisonOperator.GREATER_THAN, 1.0F ); } + + /** Check if the temperature is hot (causes snow golems to die). */ + public Builder isNotHot() { return isTemperature( ComparisonOperator.GREATER_THAN.invert(), 1.0F ); } + + private Builder isTemperature( ComparisonOperator op, float value ) { return in( new TemperatureEnvironment( op, value ) ); } + + /** Check if the biome belongs to a specific category. */ + public Builder inBiomeCategory( BiomeCategory category ) { return in( new BiomeCategoryEnvironment( category, false ) ); } + + /** Check if the biome belongs to a specific category. */ + public Builder notInBiomeCategory( BiomeCategory category ) { return in( new BiomeCategoryEnvironment( category, true ) ); } // Position-based - public Builder inStructure( Structure structure ) { return in( new StructureEnvironment( structure ) ); } + /** Check if the position is inside a particular structure. See {@link Structure}. */ + public Builder inStructure( Structure structure ) { return in( new StructureEnvironment( structure, false ) ); } - public Builder belowY( int y ) { return in( new YEnvironment( ComparisonOperator.LESS_THAN, y ) ); } + /** Check if the position is inside a particular structure. See {@link Structure}. */ + public Builder notInStructure( Structure structure ) { return in( new StructureEnvironment( structure, true ) ); } - public Builder atLeastY( int y ) { return in( new YEnvironment( ComparisonOperator.GREATER_OR_EQUAL, y ) ); } + /** Check if diamond/redstone ore can generate at the position. */ + public Builder belowDiamondLevel() { return belowY( 15 ); } // TODO update ore-based logic in 1.18 - public Builder belowSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_THAN, 0 ) ); } + /** Check if diamond/redstone ore can generate at the position. */ + public Builder aboveDiamondLevel() { return aboveY( 15 ); } - public Builder atLeastSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.GREATER_OR_EQUAL, 0 ) ); } + /** Check if gold/lapis ore can generate at the position. */ + public Builder belowGoldLevel() { return belowY( 33 ); } + + /** Check if gold/lapis ore can generate at the position. */ + public Builder aboveGoldLevel() { return aboveY( 33 ); } + + private Builder belowY( int y ) { return in( new YEnvironment( ComparisonOperator.LESS_OR_EQUAL, y ) ); } + + private Builder aboveY( int y ) { return in( new YEnvironment( ComparisonOperator.LESS_OR_EQUAL.invert(), y ) ); } + + /** Check if the position is above/below sea level. */ + public Builder belowSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_OR_EQUAL, 0 ) ); } + + /** Check if the position is above/below sea level. */ + public Builder aboveSeaLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_OR_EQUAL.invert(), 0 ) ); } + + /** Check if the position is above/below the average sea floor. */ + public Builder belowSeaFloor() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_OR_EQUAL, -18 ) ); } + + /** Check if the position is above/below the average sea floor. */ + public Builder aboveSeaFloor() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_OR_EQUAL.invert(), -18 ) ); } + + public Builder canSeeSky() { return inPositionWithState( PositionEnvironment.Value.CAN_SEE_SKY, false ); } + + public Builder cannotSeeSky() { return inPositionWithState( PositionEnvironment.Value.CAN_SEE_SKY, true ); } + + public Builder isNearVillage() { return inPositionWithState( PositionEnvironment.Value.IS_NEAR_VILLAGE, false ); } + + public Builder isNotNearVillage() { return inPositionWithState( PositionEnvironment.Value.IS_NEAR_VILLAGE, true ); } + + public Builder isNearRaid() { return inPositionWithState( PositionEnvironment.Value.IS_NEAR_RAID, false ); } + + public Builder isNotNearRaid() { return inPositionWithState( PositionEnvironment.Value.IS_NEAR_RAID, true ); } + + private Builder inPositionWithState( PositionEnvironment.Value state, boolean invert ) { return in( new PositionEnvironment( state, invert ) ); } // Time-based + /** Check if the special difficulty multiplier is above a threshold (0 - 1). */ + public Builder aboveDifficulty( float percent ) { return in( new SpecialDifficultyEnvironment( ComparisonOperator.GREATER_OR_EQUAL, percent ) ); } + + /** Check if the special difficulty multiplier is above a threshold (0 - 1). */ + public Builder belowDifficulty( float percent ) { return in( new SpecialDifficultyEnvironment( ComparisonOperator.GREATER_OR_EQUAL.invert(), percent ) ); } + + public Builder isRaining() { return inWeather( WeatherEnvironment.Value.RAIN, false ); } // same as "is not clear" + + public Builder isNotRaining() { return inWeather( WeatherEnvironment.Value.RAIN, true ); } // same as "is clear" + + public Builder isThundering() { return inWeather( WeatherEnvironment.Value.THUNDER, false ); } + + public Builder isNotThundering() { return inWeather( WeatherEnvironment.Value.THUNDER, true ); } + + private Builder inWeather( WeatherEnvironment.Value weather, boolean invert ) { return in( new WeatherEnvironment( weather, invert ) ); } + + public Builder atMaxMoonLight() { return in( new MoonPhaseEnvironment( MoonPhaseEnvironment.Value.FULL, false ) ); } + + public Builder aboveHalfMoonLight() { return fromHalfMoonLight( ComparisonOperator.GREATER_THAN ); } + + public Builder atHalfMoonLight() { return fromHalfMoonLight( ComparisonOperator.EQUAL_TO ); } + + public Builder belowHalfMoonLight() { return fromHalfMoonLight( ComparisonOperator.LESS_THAN ); } + + public Builder atNoMoonLight() { return in( new MoonPhaseEnvironment( MoonPhaseEnvironment.Value.NEW, true ) ); } + + private Builder fromHalfMoonLight( ComparisonOperator op ) { return in( new MoonBrightnessEnvironment( op, 0.5F ) ); } + + public Builder isNight() { return in( new DayTimeEnvironment( DayTimeEnvironment.Value.NIGHT, false ) ); } + + public Builder isDay() { return in( new DayTimeEnvironment( DayTimeEnvironment.Value.DAY, false ) ); } + + /** Check if the time is during a quarter of the night centered on midnight. */ + public Builder isNearMidnight() { return in( new TimeFromMidnightEnvironment( ComparisonOperator.LESS_OR_EQUAL, 1_500 ) ); } + + /** Check if the time is during a quarter of the night centered on midnight. */ + public Builder isNotNearMidnight() { return in( new TimeFromMidnightEnvironment( ComparisonOperator.LESS_OR_EQUAL.invert(), 1_500 ) ); } + + /** Check if the world time is after a certain number of days. */ + public Builder afterDays( int days ) { return in( new WorldTimeEnvironment( ComparisonOperator.GREATER_OR_EQUAL, 24_000L * days ) ); } + + /** Check if the world time is after a certain number of days. */ + public Builder beforeDays( int days ) { return in( new WorldTimeEnvironment( ComparisonOperator.GREATER_OR_EQUAL.invert(), 24_000L * days ) ); } + + /** Check if the chunk inhabited time is after a certain number of days. */ + public Builder afterDaysInChunk( int days ) { return in( new ChunkTimeEnvironment( ComparisonOperator.GREATER_OR_EQUAL, 24_000L * days ) ); } + + /** Check if the chunk inhabited time is after a certain number of days. */ + public Builder beforeDaysInChunk( int days ) { return in( new ChunkTimeEnvironment( ComparisonOperator.GREATER_OR_EQUAL.invert(), 24_000L * days ) ); } } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/WeightedList.java b/src/main/java/fathertoast/specialmobs/common/config/util/WeightedList.java index ed91404..1c34862 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/WeightedList.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/WeightedList.java @@ -15,9 +15,6 @@ import java.util.*; */ @SuppressWarnings( "unused" ) public class WeightedList { - /** The spec used by this config that defines the file's format. */ - protected final ToastConfigSpec SPEC; - /** The weighted entries in this list. */ private final List> ENTRIES; /** The total weight of all entries in this list. */ @@ -26,20 +23,20 @@ public class WeightedList { /** * Creates a new weighted list config option and registers it and any needed definitions with the spec. */ - public WeightedList( ToastConfigSpec parent, String key, T[] values, String... description ) { - this( parent, key, Arrays.asList( values ), description ); + public WeightedList( ToastConfigSpec SPEC, String key, T[] values, @Nullable String... description ) { + this( SPEC, key, Arrays.asList( values ), description ); } /** * Creates a new weighted list config option and registers it and any needed definitions with the spec. */ - public WeightedList( ToastConfigSpec parent, String key, Iterable values, String... description ) { - SPEC = parent; - + public WeightedList( ToastConfigSpec SPEC, String key, Iterable values, @Nullable String... description ) { final IntField.Range fieldRange = IntField.Range.NON_NEGATIVE; - List comment = TomlHelper.newComment( description ); - comment.add( TomlHelper.multiFieldInfo( fieldRange ) ); - SPEC.comment( comment ); + if( description != null ) { + List comment = TomlHelper.newComment( description ); + comment.add( TomlHelper.multiFieldInfo( fieldRange ) ); + SPEC.comment( comment ); + } // Define each value's weight field and connect the value to its weight in an entry List> list = new ArrayList<>(); diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/ComparisonOperator.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/ComparisonOperator.java index 9744dc0..f960a90 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/ComparisonOperator.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/ComparisonOperator.java @@ -13,56 +13,54 @@ public enum ComparisonOperator { @Override public String toString() { return LITERAL; } + /** @return A convenience method that returns the opposite comparison operator only if the passed value is true. */ + public ComparisonOperator invert( boolean invert ) { return invert ? invert() : this; } + + /** @return The opposite comparison operator. */ + public ComparisonOperator invert() { + switch( this ) { + case LESS_THAN: return GREATER_OR_EQUAL; + case LESS_OR_EQUAL: return GREATER_THAN; + case GREATER_THAN: return LESS_OR_EQUAL; + case GREATER_OR_EQUAL: return LESS_THAN; + case EQUAL_TO: return NOT_EQUAL_TO; + case NOT_EQUAL_TO: return EQUAL_TO; + } + throw new IllegalStateException( "Inversion implementation is invalid! :(" ); + } + public boolean apply( float first, float second ) { switch( this ) { - case LESS_THAN: - return first < second; - case LESS_OR_EQUAL: - return first <= second; - case GREATER_THAN: - return first > second; - case GREATER_OR_EQUAL: - return first >= second; - case EQUAL_TO: - return first == second; - case NOT_EQUAL_TO: - return first != second; + case LESS_THAN: return first < second; + case LESS_OR_EQUAL: return first <= second; + case GREATER_THAN: return first > second; + case GREATER_OR_EQUAL: return first >= second; + case EQUAL_TO: return first == second; + case NOT_EQUAL_TO: return first != second; } throw new IllegalStateException( "Float comparison implementation is invalid! :(" ); } public boolean apply( int first, int second ) { switch( this ) { - case LESS_THAN: - return first < second; - case LESS_OR_EQUAL: - return first <= second; - case GREATER_THAN: - return first > second; - case GREATER_OR_EQUAL: - return first >= second; - case EQUAL_TO: - return first == second; - case NOT_EQUAL_TO: - return first != second; + case LESS_THAN: return first < second; + case LESS_OR_EQUAL: return first <= second; + case GREATER_THAN: return first > second; + case GREATER_OR_EQUAL: return first >= second; + case EQUAL_TO: return first == second; + case NOT_EQUAL_TO: return first != second; } throw new IllegalStateException( "Integer comparison implementation is invalid! :(" ); } public boolean apply( long first, long second ) { switch( this ) { - case LESS_THAN: - return first < second; - case LESS_OR_EQUAL: - return first <= second; - case GREATER_THAN: - return first > second; - case GREATER_OR_EQUAL: - return first >= second; - case EQUAL_TO: - return first == second; - case NOT_EQUAL_TO: - return first != second; + case LESS_THAN: return first < second; + case LESS_OR_EQUAL: return first <= second; + case GREATER_THAN: return first > second; + case GREATER_OR_EQUAL: return first >= second; + case EQUAL_TO: return first == second; + case NOT_EQUAL_TO: return first != second; } throw new IllegalStateException( "Long comparison implementation is invalid! :(" ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryEnvironment.java index 2b19374..f4e1bb4 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryEnvironment.java @@ -28,8 +28,6 @@ public abstract class DynamicRegistryEnvironment extends AbstractEnvironment /** The value of ConfigUtil#DYNAMIC_REGISTRY_VERSION at the time of last poll. */ private byte version = -1; - public DynamicRegistryEnvironment( ResourceLocation regKey ) { this( regKey, false ); } - public DynamicRegistryEnvironment( ResourceLocation regKey, boolean invert ) { FIELD = null; INVERT = invert; diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryGroupEnvironment.java index c39987d..b093904 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryGroupEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryGroupEnvironment.java @@ -31,8 +31,6 @@ public abstract class DynamicRegistryGroupEnvironment extends AbstractEnviron /** The value of ConfigUtil#DYNAMIC_REGISTRY_VERSION at the time of last poll. */ private byte version = -1; - public DynamicRegistryGroupEnvironment( ResourceLocation regKey ) { this( regKey, false ); } - public DynamicRegistryGroupEnvironment( ResourceLocation regKey, boolean invert ) { FIELD = null; INVERT = invert; diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/EnumEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/EnumEnvironment.java index df3ab48..71ec5c7 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/EnumEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/EnumEnvironment.java @@ -12,8 +12,6 @@ public abstract class EnumEnvironment> extends AbstractEnviron /** The enum value for this environment. */ protected final T VALUE; - public EnumEnvironment( T value ) { this( value, false ); } - public EnumEnvironment( T value, boolean invert ) { INVERT = invert; VALUE = value; diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryEnvironment.java index 96db0d1..a638d41 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryEnvironment.java @@ -22,8 +22,6 @@ public abstract class RegistryEnvironment> exte private T registryEntry; - public RegistryEnvironment( T regEntry ) { this( regEntry, false ); } - public RegistryEnvironment( T regEntry, boolean invert ) { FIELD = null; INVERT = invert; diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryGroupEnvironment.java index 6c436b1..ef9a71d 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryGroupEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryGroupEnvironment.java @@ -24,13 +24,15 @@ public abstract class RegistryGroupEnvironment> private List registryEntries; - public RegistryGroupEnvironment( T regEntry ) { this( regEntry, false ); } - public RegistryGroupEnvironment( T regEntry, boolean invert ) { + //noinspection ConstantConditions + this( regEntry.getRegistryName(), invert ); + } + + public RegistryGroupEnvironment( ResourceLocation regKey, boolean invert ) { FIELD = null; INVERT = invert; - //noinspection ConstantConditions - NAMESPACE = regEntry.getRegistryName().toString(); + NAMESPACE = regKey.toString(); } public RegistryGroupEnvironment( AbstractConfigField field, String line ) { diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategory.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategory.java new file mode 100644 index 0000000..91c92c5 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategory.java @@ -0,0 +1,31 @@ +package fathertoast.specialmobs.common.config.util.environment.biome; + +import net.minecraft.world.biome.Biome; + +/** + * Used to wrap the vanilla enum Biome.Category so that it can be safely used in configs. + * The declared names should match the string passed into vanilla enums' constructors so that both enums serialize identically. + */ +public enum BiomeCategory { + NONE( Biome.Category.NONE ), + TAIGA( Biome.Category.TAIGA ), + EXTREME_HILLS( Biome.Category.EXTREME_HILLS ), + JUNGLE( Biome.Category.JUNGLE ), + MESA( Biome.Category.MESA ), + PLAINS( Biome.Category.PLAINS ), + SAVANNA( Biome.Category.SAVANNA ), + ICY( Biome.Category.ICY ), + THE_END( Biome.Category.THEEND ), + BEACH( Biome.Category.BEACH ), + FOREST( Biome.Category.FOREST ), + OCEAN( Biome.Category.OCEAN ), + DESERT( Biome.Category.DESERT ), + RIVER( Biome.Category.RIVER ), + SWAMP( Biome.Category.SWAMP ), + MUSHROOM( Biome.Category.MUSHROOM ), + NETHER( Biome.Category.NETHER ); + + public final Biome.Category BASE; + + BiomeCategory( Biome.Category base ) { BASE = base; } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategoryEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategoryEnvironment.java index 21d7fc9..c065716 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategoryEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategoryEnvironment.java @@ -5,40 +5,14 @@ import fathertoast.specialmobs.common.config.field.EnvironmentListField; import fathertoast.specialmobs.common.config.util.environment.EnumEnvironment; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -import net.minecraft.world.biome.Biome; import javax.annotation.Nullable; -public class BiomeCategoryEnvironment extends EnumEnvironment { - public enum Value { - NONE( Biome.Category.NONE ), - TAIGA( Biome.Category.TAIGA ), - EXTREME_HILLS( Biome.Category.EXTREME_HILLS ), - JUNGLE( Biome.Category.JUNGLE ), - MESA( Biome.Category.MESA ), - PLAINS( Biome.Category.PLAINS ), - SAVANNA( Biome.Category.SAVANNA ), - ICY( Biome.Category.ICY ), - THE_END( Biome.Category.THEEND ), - BEACH( Biome.Category.BEACH ), - FOREST( Biome.Category.FOREST ), - OCEAN( Biome.Category.OCEAN ), - DESERT( Biome.Category.DESERT ), - RIVER( Biome.Category.RIVER ), - SWAMP( Biome.Category.SWAMP ), - MUSHROOM( Biome.Category.MUSHROOM ), - NETHER( Biome.Category.NETHER ); - - public final Biome.Category BASE; - - Value( Biome.Category vanillaCat ) { BASE = vanillaCat; } - } +public class BiomeCategoryEnvironment extends EnumEnvironment { - public BiomeCategoryEnvironment( Value value ) { super( value ); } + public BiomeCategoryEnvironment( BiomeCategory value, boolean invert ) { super( value, invert ); } - public BiomeCategoryEnvironment( Value value, boolean invert ) { super( value, invert ); } - - public BiomeCategoryEnvironment( AbstractConfigField field, String line ) { super( field, line, Value.values() ); } + public BiomeCategoryEnvironment( AbstractConfigField field, String line ) { super( field, line, BiomeCategory.values() ); } /** @return The string name of this environment, as it would appear in a config file. */ @Override diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeEnvironment.java index e2ce149..9e3c704 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeEnvironment.java @@ -13,8 +13,6 @@ import javax.annotation.Nullable; public class BiomeEnvironment extends DynamicRegistryEnvironment { - public BiomeEnvironment( RegistryKey biome ) { super( biome.getRegistryName() ); } - public BiomeEnvironment( RegistryKey biome, boolean invert ) { super( biome.getRegistryName(), invert ); } public BiomeEnvironment( AbstractConfigField field, String line ) { super( field, line ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeGroupEnvironment.java index 34c5398..ede1a0f 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeGroupEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeGroupEnvironment.java @@ -4,6 +4,7 @@ import fathertoast.specialmobs.common.config.field.AbstractConfigField; import fathertoast.specialmobs.common.config.field.EnvironmentListField; import fathertoast.specialmobs.common.config.util.environment.DynamicRegistryGroupEnvironment; import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.registry.Registry; import net.minecraft.world.biome.Biome; @@ -14,9 +15,9 @@ import java.util.List; public class BiomeGroupEnvironment extends DynamicRegistryGroupEnvironment { - public BiomeGroupEnvironment( RegistryKey biome ) { super( biome.getRegistryName() ); } + public BiomeGroupEnvironment( RegistryKey biome, boolean invert ) { this( biome.getRegistryName(), invert ); } - public BiomeGroupEnvironment( RegistryKey biome, boolean invert ) { super( biome.getRegistryName(), invert ); } + public BiomeGroupEnvironment( ResourceLocation regKey, boolean invert ) { super( regKey, invert ); } public BiomeGroupEnvironment( AbstractConfigField field, String line ) { super( field, line ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeTemperatureEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeTemperatureEnvironment.java index a49182d..f7a5312 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeTemperatureEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeTemperatureEnvironment.java @@ -2,33 +2,24 @@ package fathertoast.specialmobs.common.config.util.environment.biome; import fathertoast.specialmobs.common.config.field.AbstractConfigField; import fathertoast.specialmobs.common.config.field.EnvironmentListField; -import fathertoast.specialmobs.common.config.util.environment.CompareFloatEnvironment; import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import javax.annotation.Nullable; -public class BiomeTemperatureEnvironment extends CompareFloatEnvironment { +public class BiomeTemperatureEnvironment extends TemperatureEnvironment { + + public BiomeTemperatureEnvironment( boolean freezing ) { super( freezing ); } public BiomeTemperatureEnvironment( ComparisonOperator op, float value ) { super( op, value ); } - public BiomeTemperatureEnvironment( AbstractConfigField field, String line ) { super( field, TemperatureEnvironment.handleTempInput( line ) ); } + public BiomeTemperatureEnvironment( AbstractConfigField field, String line ) { super( field, line ); } /** @return The string name of this environment, as it would appear in a config file. */ @Override public String name() { return EnvironmentListField.ENV_BIOME_TEMPERATURE; } - /** @return The string value of this environment, as it would appear in a config file. */ - @Override - public String value() { - if( COMPARATOR == ComparisonOperator.LESS_THAN && VALUE == 0.15F ) - return TemperatureEnvironment.FREEZING; - if( COMPARATOR == ComparisonOperator.GREATER_THAN && VALUE == Math.nextDown( 0.15F ) ) - return "!" + TemperatureEnvironment.FREEZING; - return super.value(); - } - /** @return Returns the actual value to compare, or Float.NaN if there isn't enough information. */ @Override public float getActual( World world, @Nullable BlockPos pos ) { diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TemperatureEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TemperatureEnvironment.java index 9b90196..200dc3d 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TemperatureEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TemperatureEnvironment.java @@ -12,15 +12,20 @@ import javax.annotation.Nullable; public class TemperatureEnvironment extends CompareFloatEnvironment { public static final String FREEZING = "freezing"; + public static final float FREEZING_POINT = 0.15F; public static String handleTempInput( String line ) { if( line.equalsIgnoreCase( FREEZING ) ) - return ComparisonOperator.LESS_THAN + " " + 0.15F; + return ComparisonOperator.LESS_THAN + " " + FREEZING_POINT; if( line.equalsIgnoreCase( "!" + FREEZING ) ) - return ComparisonOperator.GREATER_THAN + " " + Math.nextDown( 0.15F ); + return ComparisonOperator.LESS_THAN.invert() + " " + FREEZING_POINT; return line; } + public TemperatureEnvironment( boolean freezing ) { + this( ComparisonOperator.LESS_THAN.invert( !freezing ), FREEZING_POINT ); + } + public TemperatureEnvironment( ComparisonOperator op, float value ) { super( op, value ); } public TemperatureEnvironment( AbstractConfigField field, String line ) { super( field, handleTempInput( line ) ); } @@ -32,10 +37,8 @@ public class TemperatureEnvironment extends CompareFloatEnvironment { /** @return The string value of this environment, as it would appear in a config file. */ @Override public String value() { - if( COMPARATOR == ComparisonOperator.LESS_THAN && VALUE == 0.15F ) - return FREEZING; - if( COMPARATOR == ComparisonOperator.GREATER_THAN && VALUE == Math.nextDown( 0.15F ) ) - return "!" + FREEZING; + if( COMPARATOR == ComparisonOperator.LESS_THAN && VALUE == FREEZING_POINT ) return FREEZING; + if( COMPARATOR == ComparisonOperator.LESS_THAN.invert() && VALUE == FREEZING_POINT ) return "!" + FREEZING; return super.value(); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionPropertyEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionPropertyEnvironment.java index ebd9a32..50f56e1 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionPropertyEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionPropertyEnvironment.java @@ -35,8 +35,6 @@ public class DimensionPropertyEnvironment extends EnumEnvironment { - public DimensionTypeEnvironment( RegistryKey dimType ) { super( dimType.getRegistryName() ); } - public DimensionTypeEnvironment( RegistryKey dimType, boolean invert ) { super( dimType.getRegistryName(), invert ); } public DimensionTypeEnvironment( AbstractConfigField field, String line ) { super( field, line ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeGroupEnvironment.java index 9af3e68..516dd03 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeGroupEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeGroupEnvironment.java @@ -4,6 +4,7 @@ import fathertoast.specialmobs.common.config.field.AbstractConfigField; import fathertoast.specialmobs.common.config.field.EnvironmentListField; import fathertoast.specialmobs.common.config.util.environment.DynamicRegistryGroupEnvironment; import net.minecraft.util.RegistryKey; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.registry.Registry; import net.minecraft.world.DimensionType; @@ -14,9 +15,9 @@ import java.util.List; public class DimensionTypeGroupEnvironment extends DynamicRegistryGroupEnvironment { - public DimensionTypeGroupEnvironment( RegistryKey dimType ) { super( dimType.getRegistryName() ); } + public DimensionTypeGroupEnvironment( RegistryKey dimType, boolean invert ) { this( dimType.getRegistryName(), invert ); } - public DimensionTypeGroupEnvironment( RegistryKey dimType, boolean invert ) { super( dimType.getRegistryName(), invert ); } + public DimensionTypeGroupEnvironment( ResourceLocation regKey, boolean invert ) { super( regKey, invert ); } public DimensionTypeGroupEnvironment( AbstractConfigField field, String line ) { super( field, line ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/PositionEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/PositionEnvironment.java index 71c3190..ca2c7a1 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/PositionEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/PositionEnvironment.java @@ -18,7 +18,7 @@ public class PositionEnvironment extends EnumEnvironment pos != null && world instanceof ServerWorld && ((ServerWorld) world).isVillage( pos ) ), IS_NEAR_VILLAGE( ( world, pos ) -> pos != null && world instanceof ServerWorld && ((ServerWorld) world).isCloseToVillage( pos, 3 ) ), - IS_RAIDED( ( world, pos ) -> pos != null && world instanceof ServerWorld && ((ServerWorld) world).isRaided( pos ) ), + IS_NEAR_RAID( ( world, pos ) -> pos != null && world instanceof ServerWorld && ((ServerWorld) world).isRaided( pos ) ), IS_IN_WATER( ( world, pos ) -> pos != null && world.getFluidState( pos ).is( FluidTags.WATER ) ), IS_IN_LAVA( ( world, pos ) -> pos != null && world.getFluidState( pos ).is( FluidTags.LAVA ) ), IS_IN_FLUID( ( world, pos ) -> pos != null && !world.getFluidState( pos ).isEmpty() ), @@ -31,8 +31,6 @@ public class PositionEnvironment extends EnumEnvironment> { - public StructureEnvironment( Structure structure ) { super( structure ); } - public StructureEnvironment( Structure structure, boolean invert ) { super( structure, invert ); } public StructureEnvironment( AbstractConfigField field, String line ) { super( field, line ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureGroupEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureGroupEnvironment.java index 584ee56..819cc12 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureGroupEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureGroupEnvironment.java @@ -3,6 +3,7 @@ package fathertoast.specialmobs.common.config.util.environment.position; import fathertoast.specialmobs.common.config.field.AbstractConfigField; import fathertoast.specialmobs.common.config.field.EnvironmentListField; import fathertoast.specialmobs.common.config.util.environment.RegistryGroupEnvironment; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.gen.feature.structure.Structure; @@ -16,9 +17,9 @@ import java.util.List; public class StructureGroupEnvironment extends RegistryGroupEnvironment> { - public StructureGroupEnvironment( Structure biome ) { super( biome ); } + public StructureGroupEnvironment( Structure structure, boolean invert ) { super( structure, invert ); } - public StructureGroupEnvironment( Structure biome, boolean invert ) { super( biome, invert ); } + public StructureGroupEnvironment( ResourceLocation regKey, boolean invert ) { super( regKey, invert ); } public StructureGroupEnvironment( AbstractConfigField field, String line ) { super( field, line ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/ChunkTimeEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/ChunkTimeEnvironment.java new file mode 100644 index 0000000..3be2629 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/ChunkTimeEnvironment.java @@ -0,0 +1,33 @@ +package fathertoast.specialmobs.common.config.util.environment.time; + +import fathertoast.specialmobs.common.config.field.AbstractConfigField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; +import fathertoast.specialmobs.common.config.util.environment.CompareLongEnvironment; +import fathertoast.specialmobs.common.config.util.environment.ComparisonOperator; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class ChunkTimeEnvironment extends CompareLongEnvironment { + + public ChunkTimeEnvironment( ComparisonOperator op, long value ) { super( op, value ); } + + public ChunkTimeEnvironment( AbstractConfigField field, String line ) { super( field, line ); } + + /** @return The minimum value that can be given to the value. */ + @Override + protected long getMinValue() { return 0L; } + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_CHUNK_TIME; } + + /** @return Returns the actual value to compare, or null if there isn't enough information. */ + @Override + public Long getActual( World world, @Nullable BlockPos pos ) { + // Ignore deprecation; this is intentionally the same method used by World#getCurrentDifficultyAt + //noinspection deprecation + return pos == null || !world.hasChunkAt( pos ) ? null : world.getChunkAt( pos ).getInhabitedTime(); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DayTimeEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DayTimeEnvironment.java index 98f038d..1a9a9b9 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DayTimeEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DayTimeEnvironment.java @@ -27,8 +27,6 @@ public class DayTimeEnvironment extends EnumEnvironment mobFamily, World world, BlockPos entityPos ) { - return world.random.nextFloat() < mobFamily.config.GENERAL.specialVariantChance.get(); //TODO environment exceptions + return world.random.nextDouble() < mobFamily.config.GENERAL.specialVariantChance.get( world, entityPos ); } /** @return True if a mob should be replaced. */ From 4257557dafc8aa619ce92c953e9b58d7f44e11c8 Mon Sep 17 00:00:00 2001 From: Sarinsa Date: Mon, 18 Jul 2022 17:02:48 +0200 Subject: [PATCH 07/16] Removed unnecessary functionality from IncorporealFireballEntity --- .../common/core/register/SMEntities.java | 6 +- .../ghast/CorporealShiftGhastEntity.java | 44 ++++++-- ...ty.java => IncorporealFireballEntity.java} | 104 ++++++------------ .../common/network/work/ServerWork.java | 4 +- 4 files changed, 72 insertions(+), 86 deletions(-) rename src/main/java/fathertoast/specialmobs/common/entity/projectile/{CorporealShiftFireballEntity.java => IncorporealFireballEntity.java} (59%) diff --git a/src/main/java/fathertoast/specialmobs/common/core/register/SMEntities.java b/src/main/java/fathertoast/specialmobs/common/core/register/SMEntities.java index 9e3e246..ff63b3c 100644 --- a/src/main/java/fathertoast/specialmobs/common/core/register/SMEntities.java +++ b/src/main/java/fathertoast/specialmobs/common/core/register/SMEntities.java @@ -3,7 +3,7 @@ package fathertoast.specialmobs.common.core.register; import fathertoast.specialmobs.common.bestiary.MobFamily; import fathertoast.specialmobs.common.config.util.ConfigDrivenAttributeModifierMap; import fathertoast.specialmobs.common.core.SpecialMobs; -import fathertoast.specialmobs.common.entity.projectile.CorporealShiftFireballEntity; +import fathertoast.specialmobs.common.entity.projectile.IncorporealFireballEntity; import fathertoast.specialmobs.common.util.AnnotationHelper; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityClassification; @@ -18,8 +18,8 @@ public class SMEntities { public static final DeferredRegister> REGISTRY = DeferredRegister.create( ForgeRegistries.ENTITIES, SpecialMobs.MOD_ID ); // Misc entities - public static final RegistryObject> CORPOREAL_FIREBALL = register( "corporeal_shift_fireball", - EntityType.Builder.of( CorporealShiftFireballEntity::new, EntityClassification.MISC ).sized( 1.0F, 1.0F ).clientTrackingRange( 4 ).updateInterval( 3 ) ); + public static final RegistryObject> CORPOREAL_FIREBALL = register( "incorporeal_fireball", + EntityType.Builder.of( IncorporealFireballEntity::new, EntityClassification.MISC ).sized( 1.0F, 1.0F ).clientTrackingRange( 4 ).updateInterval( 3 ) ); /** Registers an entity type to the deferred register. */ public static RegistryObject> register( String name, EntityType.Builder builder ) { diff --git a/src/main/java/fathertoast/specialmobs/common/entity/ghast/CorporealShiftGhastEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/ghast/CorporealShiftGhastEntity.java index 57c3af1..8cc1360 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/ghast/CorporealShiftGhastEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/ghast/CorporealShiftGhastEntity.java @@ -4,12 +4,14 @@ import fathertoast.specialmobs.common.bestiary.BestiaryInfo; import fathertoast.specialmobs.common.bestiary.MobFamily; import fathertoast.specialmobs.common.bestiary.SpecialMob; import fathertoast.specialmobs.common.core.register.SMItems; -import fathertoast.specialmobs.common.entity.projectile.CorporealShiftFireballEntity; +import fathertoast.specialmobs.common.entity.projectile.IncorporealFireballEntity; import fathertoast.specialmobs.common.util.References; import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.entity.projectile.FireballEntity; import net.minecraft.nbt.CompoundNBT; import net.minecraft.network.datasync.DataParameter; import net.minecraft.network.datasync.DataSerializers; @@ -90,7 +92,18 @@ public class CorporealShiftGhastEntity extends _SpecialGhastEntity { private void spawnShiftSmoke( ServerWorld world ) { world.sendParticles( ParticleTypes.CLOUD, this.getX(), this.getY(), this.getZ(), 25, 0.0, 0.0, 0.0, 0.4 ); } - + + @Override + public boolean isPushable() { + return isCorporeal() && super.isPushable(); + } + + @Override + protected void doPush(Entity entity) { + if (isCorporeal()) + super.doPush(entity); + } + public boolean isCorporeal() { return entityData.get( CORPOREAL ); } private void setCorporeal( boolean value ) { entityData.set( CORPOREAL, value ); } @@ -109,14 +122,25 @@ public class CorporealShiftGhastEntity extends _SpecialGhastEntity { double dX = target.getX() - (getX() + lookVec.x) + getRandom().nextGaussian() * accelVariance; double dY = target.getY( 0.5 ) - (0.5 + getY( 0.5 )); double dZ = target.getZ() - (getZ() + lookVec.z) + getRandom().nextGaussian() * accelVariance; - - final CorporealShiftFireballEntity fireball = new CorporealShiftFireballEntity( level, this, dX, dY, dZ ); - fireball.explosionPower = getVariantExplosionPower( getExplosionPower() ); - fireball.setPos( - getX() + lookVec.x, - getY( 0.5 ) + 0.5, - getZ() + lookVec.z ); - level.addFreshEntity( fireball ); + + if (isCorporeal()) { + FireballEntity fireball = new FireballEntity( level, this, dX, dY, dZ ); + fireball.explosionPower = getVariantExplosionPower( getExplosionPower() ); + fireball.setPos( + getX() + lookVec.x, + getY( 0.5 ) + 0.5, + getZ() + lookVec.z ); + level.addFreshEntity( fireball ); + } + else { + IncorporealFireballEntity fireball = new IncorporealFireballEntity( level, this, dX, dY, dZ ); + fireball.explosionPower = getVariantExplosionPower( getExplosionPower() ); + fireball.setPos( + getX() + lookVec.x, + getY( 0.5 ) + 0.5, + getZ() + lookVec.z ); + level.addFreshEntity( fireball ); + } } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/projectile/CorporealShiftFireballEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/projectile/IncorporealFireballEntity.java similarity index 59% rename from src/main/java/fathertoast/specialmobs/common/entity/projectile/CorporealShiftFireballEntity.java rename to src/main/java/fathertoast/specialmobs/common/entity/projectile/IncorporealFireballEntity.java index e107a3c..733309f 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/projectile/CorporealShiftFireballEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/projectile/IncorporealFireballEntity.java @@ -1,7 +1,6 @@ package fathertoast.specialmobs.common.entity.projectile; import fathertoast.specialmobs.common.bestiary.SpecialMob; -import fathertoast.specialmobs.common.core.SpecialMobs; import fathertoast.specialmobs.common.core.register.SMEntities; import fathertoast.specialmobs.common.core.register.SMItems; import fathertoast.specialmobs.common.entity.ghast.CorporealShiftGhastEntity; @@ -12,12 +11,8 @@ import net.minecraft.entity.LivingEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.projectile.AbstractFireballEntity; import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; import net.minecraft.nbt.CompoundNBT; import net.minecraft.network.IPacket; -import net.minecraft.network.datasync.DataParameter; -import net.minecraft.network.datasync.DataSerializers; -import net.minecraft.network.datasync.EntityDataManager; import net.minecraft.util.DamageSource; import net.minecraft.util.SoundEvents; import net.minecraft.util.math.EntityRayTraceResult; @@ -25,16 +20,16 @@ import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.vector.Vector3d; import net.minecraft.world.Explosion; import net.minecraft.world.World; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.util.Constants; import net.minecraftforge.event.ForgeEventFactory; import net.minecraftforge.fml.network.NetworkHooks; import javax.annotation.Nullable; -public class CorporealShiftFireballEntity extends AbstractFireballEntity { - - private static final DataParameter CORPOREAL = EntityDataManager.defineId( CorporealShiftFireballEntity.class, DataSerializers.BOOLEAN ); - +public class IncorporealFireballEntity extends AbstractFireballEntity { + public int explosionPower = 1; private boolean shouldExplode = false; @@ -42,49 +37,31 @@ public class CorporealShiftFireballEntity extends AbstractFireballEntity { private LivingEntity target; - public CorporealShiftFireballEntity( EntityType entityType, World world ) { + public IncorporealFireballEntity(EntityType entityType, World world ) { super( entityType, world ); } - public CorporealShiftFireballEntity( World world, CorporealShiftGhastEntity ghast, double x, double y, double z ) { + public IncorporealFireballEntity(World world, CorporealShiftGhastEntity ghast, double x, double y, double z ) { super( SMEntities.CORPOREAL_FIREBALL.get(), ghast, x, y, z, world ); - setCorporeal( ghast.isCorporeal() ); target = ghast.getTarget(); - setItem( isCorporeal() ? new ItemStack( Items.FIRE_CHARGE ) : new ItemStack( SMItems.INCORPOREAL_FIREBALL.get() ) ); } - public CorporealShiftFireballEntity( World world, PlayerEntity owner, LivingEntity target, double x, double y, double z ) { + public IncorporealFireballEntity(World world, PlayerEntity owner, LivingEntity target, double x, double y, double z ) { super( SMEntities.CORPOREAL_FIREBALL.get(), owner, x, y, z, world ); - setCorporeal( false ); this.target = target; - setItem( new ItemStack( SMItems.INCORPOREAL_FIREBALL.get() ) ); } @SpecialMob.LanguageProvider public static String[] getTranslations( String langKey ) { - return References.translations( langKey, "Corporeal Shift Fireball", + return References.translations( langKey, "Incorporeal Fireball", "", "", "", "", "", "" );//TODO } - @Override - protected void defineSynchedData() { - super.defineSynchedData(); - entityData.define( CORPOREAL, true ); - } - - public boolean isCorporeal() { - return entityData.get( CORPOREAL ); - } - - public void setCorporeal( boolean corporeal ) { - entityData.set( CORPOREAL, corporeal ); - } - @Override public void tick() { super.tick(); - if( !level.isClientSide && !isCorporeal() ) { + if( !level.isClientSide ) { // Fizzle out and die when the target is dead or lost, // or else the fireball goes bonkers. if( target == null || !target.isAlive() ) { @@ -112,19 +89,12 @@ public class CorporealShiftFireballEntity extends AbstractFireballEntity { @Override protected boolean shouldBurn() { - // Hee hee hee haw - return isCorporeal(); + return false; } @Override protected void onHit( RayTraceResult traceResult ) { super.onHit( traceResult ); - - // Only go boom if the fireball is corporeal. - // If not, pass through blocks to be a menace. - if( !level.isClientSide && isCorporeal() ) { - shouldExplode = true; - } } @Override @@ -133,52 +103,45 @@ public class CorporealShiftFireballEntity extends AbstractFireballEntity { if( !this.level.isClientSide ) { Entity target = traceResult.getEntity(); - Entity owner = getOwner(); - - if( !isCorporeal() ) { - // TODO - Figure out why this is cringe - // TODO part 2. - What the fuck - // TODO part 3. - HELP - SpecialMobs.LOG.info( "X={}, XO={}", target.getX(), target.xo ); - SpecialMobs.LOG.info( "Z={}, ZO={}", target.getZ(), target.zo ); - - if( target.getX() != target.xo || target.getY() != target.yo || target.getZ() != target.zo ) { - explode(); - } - else { - playSound( SoundEvents.FIRE_EXTINGUISH, 1.0F, 1.0F ); - remove(); - } + + boolean fizzle; + + if ( target instanceof PlayerEntity ) { + // TODO - Implement player-specific checks + fizzle = true; } else { - target.hurt( DamageSource.fireball( this, owner ), 6.0F ); - - if( owner instanceof LivingEntity ) { - doEnchantDamageEffects( (LivingEntity) owner, target ); + if (target.getX() != target.xo || target.getY() != target.yo || target.getZ() != target.zo) { + explode(); + return; } + fizzle = true; + } + if (fizzle) { + playSound( SoundEvents.FIRE_EXTINGUISH, 1.0F, 1.0F ); + remove(); } } } @Override public boolean hurt( DamageSource damageSource, float damage ) { - if( !isCorporeal() ) { - if( isInvulnerableTo( damageSource ) || damageSource.isFire() ) { - return false; - } - shouldExplode = true; - return true; - } - else { - return super.hurt( damageSource, damage ); + if( isInvulnerableTo( damageSource ) || damageSource.isFire() ) { + return false; } + shouldExplode = true; + return true; + } + + @OnlyIn(Dist.CLIENT) + public ItemStack getItem() { + return new ItemStack(SMItems.INCORPOREAL_FIREBALL.get()); } @Override public void addAdditionalSaveData( CompoundNBT compoundNBT ) { super.addAdditionalSaveData( compoundNBT ); compoundNBT.putInt( "ExplosionPower", explosionPower ); - compoundNBT.putBoolean( "Corporeal", isCorporeal() ); compoundNBT.putInt( "TargetId", target == null ? -1 : target.getId() ); } @@ -188,7 +151,6 @@ public class CorporealShiftFireballEntity extends AbstractFireballEntity { if( compoundNBT.contains( "ExplosionPower", Constants.NBT.TAG_ANY_NUMERIC ) ) { explosionPower = compoundNBT.getInt( "ExplosionPower" ); } - entityData.set( CORPOREAL, compoundNBT.getBoolean( "Corporeal" ) ); if( compoundNBT.contains( "TargetId", Constants.NBT.TAG_ANY_NUMERIC ) ) { Entity entity = level.getEntity( compoundNBT.getInt( "TargetId" ) ); diff --git a/src/main/java/fathertoast/specialmobs/common/network/work/ServerWork.java b/src/main/java/fathertoast/specialmobs/common/network/work/ServerWork.java index b15773f..cca5778 100644 --- a/src/main/java/fathertoast/specialmobs/common/network/work/ServerWork.java +++ b/src/main/java/fathertoast/specialmobs/common/network/work/ServerWork.java @@ -1,6 +1,6 @@ package fathertoast.specialmobs.common.network.work; -import fathertoast.specialmobs.common.entity.projectile.CorporealShiftFireballEntity; +import fathertoast.specialmobs.common.entity.projectile.IncorporealFireballEntity; import fathertoast.specialmobs.common.network.message.C2SSpawnIncorporealFireball; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; @@ -30,7 +30,7 @@ public class ServerWork { return; LivingEntity livingEntity = (LivingEntity) entity; - CorporealShiftFireballEntity fireballEntity = new CorporealShiftFireballEntity(world, player, livingEntity, player.getX(), player.getEyeY(), player.getZ()); + IncorporealFireballEntity fireballEntity = new IncorporealFireballEntity(world, player, livingEntity, player.getX(), player.getEyeY(), player.getZ()); world.addFreshEntity(fireballEntity); } } From bf0931b4f84b2ad75070cff7b2bbf5d6913e5d81 Mon Sep 17 00:00:00 2001 From: FatherToast Date: Tue, 19 Jul 2022 19:43:31 -0500 Subject: [PATCH 08/16] Themes --- .../common/bestiary/BestiaryInfo.java | 83 +++++++++++++++---- .../common/bestiary/MobFamily.java | 29 +------ .../common/config/family/FamilyConfig.java | 33 +++++--- .../config/field/EnvironmentListField.java | 2 +- .../common/config/util/EnvironmentEntry.java | 6 ++ 5 files changed, 103 insertions(+), 50 deletions(-) diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java b/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java index bbd0270..e8db1a2 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java @@ -1,6 +1,8 @@ package fathertoast.specialmobs.common.bestiary; +import fathertoast.specialmobs.common.config.field.DoubleField; import fathertoast.specialmobs.common.config.util.*; +import fathertoast.specialmobs.common.config.util.environment.biome.BiomeCategory; import fathertoast.specialmobs.common.core.SpecialMobs; import fathertoast.specialmobs.common.util.References; import net.minecraft.block.Block; @@ -14,7 +16,9 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.registries.ForgeRegistries; import javax.annotation.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; /** * This class serves solely to store data for mob species in an organized way, providing builder methods as applicable. @@ -23,24 +27,75 @@ import java.util.*; public class BestiaryInfo { public enum DefaultWeight { - DEFAULT( 600 ), - DISABLED( 0 ), - LOWEST( DEFAULT.value / 8 ), - LOW( DEFAULT.value / 4 ), - HIGH( DEFAULT.value * 4 ), - HIGHEST( DEFAULT.value * 8 ); + DEFAULT( 60.0 ), + DISABLED( 0.0 ), + LOWEST( DEFAULT.value / 8.0 ), + LOW( DEFAULT.value / 4.0 ), + HIGH( DEFAULT.value * 4.0 ), + HIGHEST( DEFAULT.value * 8.0 ); - public final int value; + public final double value; - DefaultWeight( int v ) { value = v; } + DefaultWeight( double v ) { value = v; } } public enum Theme { - NONE, - FIRE, ICE, - DESERT, WATER, - FOREST, MOUNTAIN, - FISHING + NONE( new EnvironmentList() ), + FIRE( new EnvironmentList( + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inUltraWarmDimension().build(), + EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).isRaining().canSeeSky().notInDryBiome().build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).isHot().build(), + EnvironmentEntry.builder( DefaultWeight.HIGH.value ).isWarm().build(), + EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).isFreezing().build() + ) ), + ICE( new EnvironmentList( + EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).inUltraWarmDimension().build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).isFreezing().build(), + EnvironmentEntry.builder( DefaultWeight.LOW.value ).isWarm().build(), + EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).isHot().build() + ) ), + DESERT( new EnvironmentList( + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inUltraWarmDimension().build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inDryBiome().build(), + EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).inHumidBiome().build(), + EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).inBiomeCategory( BiomeCategory.OCEAN ).build(), + EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).inBiomeCategory( BiomeCategory.RIVER ).build(), + EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).isRaining().canSeeSky().build() + ) ), + WATER( new EnvironmentList( + EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).inUltraWarmDimension().build(), + EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).inDryBiome().build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inHumidBiome().build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.OCEAN ).build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.RIVER ).build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).isRaining().canSeeSky().build() + ) ), + FOREST( new EnvironmentList( + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.TAIGA ).build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.JUNGLE ).build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.FOREST ).build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.SWAMP ).build(), + EnvironmentEntry.builder( DefaultWeight.HIGH.value ).atMaxMoonLight().build() + ) ), + MOUNTAIN( new EnvironmentList( + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.EXTREME_HILLS ).build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.MESA ).build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).aboveMountainLevel().build(), + EnvironmentEntry.builder( DefaultWeight.HIGH.value ).atNoMoonLight().build(), + EnvironmentEntry.builder( DefaultWeight.LOW.value ).belowSeaFloor().build() + ) ), + FISHING( new EnvironmentList( + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.OCEAN ).build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.RIVER ).build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.BEACH ).build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.BEACH ).build(), + EnvironmentEntry.builder( DefaultWeight.HIGH.value ).atMaxMoonLight().build(), + EnvironmentEntry.builder( DefaultWeight.HIGH.value ).isRaining().notInDryBiome().build() + ) ); + + public final EnvironmentList value; + + Theme( EnvironmentList v ) { value = v.setRange( DoubleField.Range.NON_NEGATIVE ); } } /** The spot color for spawn eggs of this species. The base color is determined by the family. */ diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java index affbf32..92f05c7 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java @@ -192,31 +192,10 @@ public class MobFamily { } /** Pick a new species from this family, based on the location. */ - public Species nextVariant( World world, BlockPos pos ) { - // Build weights for the current location - int totalWeight = 0; - final int[] variantWeights = new int[variants.length]; - for( int i = 0; i < variants.length; i++ ) { - final int weight = config.GENERAL.specialVariantWeights[i].get(); //TODO environment exceptions - if( weight > 0 ) { - totalWeight += weight; - variantWeights[i] = weight; - } - } - - // Pick one item at random - if( totalWeight > 0 ) { - int weight = world.random.nextInt( totalWeight ); - for( int i = 0; i < variants.length; i++ ) { - if( variantWeights[i] > 0 ) { - weight -= variantWeights[i]; - if( weight < 0 ) { - return variants[i]; - } - } - } - } - return vanillaReplacement; + public Species nextVariant( World world, @Nullable BlockPos pos ) { + final Species species = config.GENERAL.specialVariantList.next( world.random, world, pos ); + //noinspection unchecked + return species == null ? vanillaReplacement : (Species) species; } diff --git a/src/main/java/fathertoast/specialmobs/common/config/family/FamilyConfig.java b/src/main/java/fathertoast/specialmobs/common/config/family/FamilyConfig.java index 1d203a9..00dd6bb 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/family/FamilyConfig.java +++ b/src/main/java/fathertoast/specialmobs/common/config/family/FamilyConfig.java @@ -5,7 +5,6 @@ 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; @@ -56,7 +55,7 @@ public class FamilyConfig extends Config.AbstractConfig { public final DoubleField.EnvironmentSensitive specialVariantChance; - public final IntField[] specialVariantWeights; + public final DoubleField.EnvironmentSensitiveWeightedList> specialVariantList; General( ToastConfigSpec parent, MobFamily family, double variantChance ) { super( parent, "general", @@ -90,20 +89,34 @@ public class FamilyConfig extends Config.AbstractConfig { SPEC.newLine(); List comment; + final DoubleField[] baseWeights = new DoubleField[family.variants.length]; + final EnvironmentListField[] weightExceptions = new EnvironmentListField[family.variants.length]; - // TODO consider wrapping this all up into an 'environment sensitive weighted list' config object? seems handy comment = TomlHelper.newComment( "The weight of each " + ConfigUtil.camelCaseToLowerSpace( family.name ) + " species to be chosen as the replacement when", family.configName + " spawn as special variants. Higher weight is more common." ); - comment.add( TomlHelper.multiFieldInfo( IntField.Range.NON_NEGATIVE ) ); + comment.add( TomlHelper.multiFieldInfo( DoubleField.Range.NON_NEGATIVE ) ); SPEC.comment( comment ); - specialVariantWeights = new IntField[family.variants.length]; - for( int i = 0; i < specialVariantWeights.length; i++ ) { - specialVariantWeights[i] = SPEC.define( new IntField( - "weight." + ConfigUtil.camelCaseToLowerUnderscore( family.variants[i].specialVariantName ), - family.variants[i].bestiaryInfo.defaultWeight.value, IntField.Range.NON_NEGATIVE, (String[]) null ) ); + for( int i = 0; i < family.variants.length; i++ ) { + baseWeights[i] = SPEC.define( new DoubleField( + "weight." + ConfigUtil.camelCaseToLowerUnderscore( family.variants[i].specialVariantName ) + ".base", + family.variants[i].bestiaryInfo.defaultWeight.value, DoubleField.Range.NON_NEGATIVE, (String[]) null ) ); } - // TODO special variant weights exceptions + + SPEC.newLine(); + + comment = TomlHelper.newComment( + "The weight of each " + ConfigUtil.camelCaseToLowerSpace( family.name ) + " species to be chosen as the replacement when", + family.configName + " spawn as special variants when specific environmental conditions are met. Higher weight is more common." ); + comment.add( TomlHelper.multiFieldInfo( DoubleField.Range.NON_NEGATIVE ) ); + SPEC.comment( comment ); + for( int i = 0; i < family.variants.length; i++ ) { + weightExceptions[i] = SPEC.define( new EnvironmentListField( + "weight." + ConfigUtil.camelCaseToLowerUnderscore( family.variants[i].specialVariantName ) + ".exceptions", + family.variants[i].bestiaryInfo.theme.value, (String[]) null ) ); + } + + specialVariantList = new DoubleField.EnvironmentSensitiveWeightedList<>( family.variants, baseWeights, weightExceptions ); } } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java b/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java index 3481ea3..21abdb5 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java @@ -134,7 +134,7 @@ public class EnvironmentListField extends GenericField { } /** Creates a new field. */ - public EnvironmentListField( String key, EnvironmentList defaultValue, String... description ) { + public EnvironmentListField( String key, EnvironmentList defaultValue, @Nullable String... description ) { super( key, defaultValue, description ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java index 4b66e24..f4ccdb6 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java @@ -215,6 +215,12 @@ public class EnvironmentEntry { /** Check if the position is above/below the average sea floor. */ public Builder aboveSeaFloor() { return in( new YFromSeaEnvironment( ComparisonOperator.LESS_OR_EQUAL.invert(), -18 ) ); } + /** Check if the position is above/below 'mountain level' - that is, high enough to die from falling to sea level. */ + public Builder aboveMountainLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.GREATER_OR_EQUAL, 24 ) ); } + + /** Check if the position is above/below 'mountain level' - that is, high enough to die from falling to sea level. */ + public Builder belowMountainLevel() { return in( new YFromSeaEnvironment( ComparisonOperator.GREATER_OR_EQUAL.invert(), 24 ) ); } + public Builder canSeeSky() { return inPositionWithState( PositionEnvironment.Value.CAN_SEE_SKY, false ); } public Builder cannotSeeSky() { return inPositionWithState( PositionEnvironment.Value.CAN_SEE_SKY, true ); } From 35388dea281fad2a4b08df5065c87d76e5f15f3f Mon Sep 17 00:00:00 2001 From: FatherToast Date: Tue, 19 Jul 2022 20:52:13 -0500 Subject: [PATCH 09/16] Tweaks --- .../fathertoast/specialmobs/common/bestiary/BestiaryInfo.java | 4 ++-- .../specialmobs/common/config/util/EnvironmentEntry.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java b/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java index e8db1a2..88e9e8a 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java @@ -31,8 +31,8 @@ public class BestiaryInfo { DISABLED( 0.0 ), LOWEST( DEFAULT.value / 8.0 ), LOW( DEFAULT.value / 4.0 ), - HIGH( DEFAULT.value * 4.0 ), - HIGHEST( DEFAULT.value * 8.0 ); + HIGH( DEFAULT.value * 2.5 ), + HIGHEST( DEFAULT.value * 5.0 ); public final double value; diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java index f4ccdb6..a0bc5df 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java @@ -262,7 +262,7 @@ public class EnvironmentEntry { public Builder belowHalfMoonLight() { return fromHalfMoonLight( ComparisonOperator.LESS_THAN ); } - public Builder atNoMoonLight() { return in( new MoonPhaseEnvironment( MoonPhaseEnvironment.Value.NEW, true ) ); } + public Builder atNoMoonLight() { return in( new MoonPhaseEnvironment( MoonPhaseEnvironment.Value.NEW, false ) ); } private Builder fromHalfMoonLight( ComparisonOperator op ) { return in( new MoonBrightnessEnvironment( op, 0.5F ) ); } From c88d9c154359536d4b5dc856eba3e500ecc4e459 Mon Sep 17 00:00:00 2001 From: FatherToast Date: Thu, 21 Jul 2022 14:12:54 -0500 Subject: [PATCH 10/16] Un-bork piglins --- .../specialmobs/client/ClientRegister.java | 4 ++++ .../BruteZombifiedPiglinEntity.java | 2 +- .../VampireZombifiedPiglinEntity.java | 2 +- .../entity/zombified_piglin/brute.png | Bin 14696 -> 3724 bytes .../entity/zombified_piglin/brute_overlay.png | Bin 13737 -> 0 bytes .../entity/zombified_piglin/hungry.png | Bin 6739 -> 2892 bytes .../entity/zombified_piglin/plague.png | Bin 7260 -> 3720 bytes .../entity/zombified_piglin/vampire.png | Bin 8346 -> 2022 bytes .../zombified_piglin/vampire_overlay.png | Bin 7768 -> 0 bytes 9 files changed, 6 insertions(+), 2 deletions(-) delete mode 100644 src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/brute_overlay.png delete mode 100644 src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/vampire_overlay.png diff --git a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java index 1b9eb2b..3712786 100644 --- a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java +++ b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java @@ -8,6 +8,7 @@ import fathertoast.specialmobs.common.entity.ghast.CorporealShiftGhastEntity; import fathertoast.specialmobs.common.entity.skeleton.NinjaSkeletonEntity; import fathertoast.specialmobs.common.entity.witherskeleton.NinjaWitherSkeletonEntity; import fathertoast.specialmobs.common.entity.zombie.MadScientistZombieEntity; +import fathertoast.specialmobs.common.entity.zombifiedpiglin.VampireZombifiedPiglinEntity; import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.ItemRenderer; @@ -56,8 +57,11 @@ public class ClientRegister { // Species overrides registerSpeciesRenderer( MadScientistZombieEntity.SPECIES, SpecialZombieVillagerRenderer::new ); + registerSpeciesRenderer( VampireZombifiedPiglinEntity.SPECIES, SpecialPiglinRenderer::newBothEars ); + registerSpeciesRenderer( NinjaSkeletonEntity.SPECIES, NinjaSkeletonRenderer::new ); registerSpeciesRenderer( NinjaWitherSkeletonEntity.SPECIES, NinjaSkeletonRenderer::new ); + registerSpeciesRenderer( CorporealShiftGhastEntity.SPECIES, CorporealShiftGhastRenderer::new ); // Other diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/BruteZombifiedPiglinEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/BruteZombifiedPiglinEntity.java index ee60f43..00abdca 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/BruteZombifiedPiglinEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/BruteZombifiedPiglinEntity.java @@ -32,7 +32,7 @@ public class BruteZombifiedPiglinEntity extends _SpecialZombifiedPiglinEntity { @SpecialMob.BestiaryInfoSupplier public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { bestiaryInfo.color( 0xFFF87E ) - .uniqueTextureWithOverlay() + .uniqueTextureBaseOnly() .size( 1.2F, 0.7F, 2.35F ) .addExperience( 2 ) .addToAttribute( Attributes.MAX_HEALTH, 10.0 ).addToAttribute( Attributes.ARMOR, 10.0 ); diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/VampireZombifiedPiglinEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/VampireZombifiedPiglinEntity.java index 82de371..e562aeb 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/VampireZombifiedPiglinEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/VampireZombifiedPiglinEntity.java @@ -31,7 +31,7 @@ public class VampireZombifiedPiglinEntity extends _SpecialZombifiedPiglinEntity @SpecialMob.BestiaryInfoSupplier public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { bestiaryInfo.color( 0x000000 ).weight( BestiaryInfo.DefaultWeight.LOW ) - .uniqueTextureWithOverlay() + .uniqueTextureBaseOnly() .addExperience( 4 ) .addToAttribute( Attributes.MAX_HEALTH, 10.0 ); } diff --git a/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/brute.png b/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/brute.png index 23543ed7968041c2707ac2119f8737be446f7b4f..0e1059da0ad3c2868dd8bc894fee2f4a2ad628fb 100644 GIT binary patch delta 3706 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 YFVW8X8bKA{UjP6A07*qoM6N<$f*7OfvH$=8 literal 14696 zcmbVSQ*TjyWzn~&EW3BKCX z&uuSdzKA_dAN$WLOe53Lm_cw9Z!b4?3qKODhw#N0XM*yiXHzC-h{)gDKAqM(^!dLG zyY=}$4ZF;}SN6AMcXRJH6~5}{+%}K>Y`L3euUYQ=dS2}aHqJ-y-}APu-L7q)bKP7> z&}mgS+RF1vUfkk&nM|8UVf9n`GV-c?TzYY&b=g0XjckB3{d|rpq zlsOCTc!SXkb*&mg@l$`1olb5mILx><>_&vF5T^~+b+~)kwLQ~qyWc-;VDkidbckCOUy3aBu!T)1+Gy*!x zvQ1?6lIOI# zrR$A49*m{yw5n)nT|2jEC`r`@wzMp-pA?7_(mu~uo!30`K{DXM^SXQ5k7BX>;avFB z^oMKx!ijdkyd*_k)1_r;Wz}$0_U*c{YTf3UHkz5C6r?vCIVYbuHY1E?B9&;NGmKe?2nUW?T(|hp9fxC*R?YH%e)k!9W|b2t_``gIMTl%8=9RE_Aj`lJ1|KlOx-xD`l_B> z&V1^n9*wuI%zKvnf{@d>qg(DI3?8G?fVXKNuG(atSLv%3CCzU9oLPqRt4+uNnlwD~ z+U_KX&4?f7A0CE0@75l!^s*mnwOgmIxc&hdF7#7Y&kfV$3J-Eu=kRT83f(l8gnsmS z+tEz@g&LZAHZQu5{FWhuURyiJ*PGT*((GrEJX`Mih}_)*)&Q!TxPx^u;M?M9~}X zMA-F!3BsNkkZ3{q@pFCU;u^li*u^(5IIIhQu&w0RlQ8WFqw;gvW%%%3X9?=C6Q)({mIqr?3tqK74hNLY$e=%J)u41gkfe z8QFKfA;n4BJ8A_F3XE7D$Hre#hBE`bVaKqsF>NRt=q#v}?uCti7Tj(q$*d$u+7Af$_EF`$z{3R|&s^U|H%-pvA8bfI zW^3bxuDdab;q3lSGDcw~QGcSUyzzEj+4M(O-{^Hf8Rkgf=S6<} zfQ|V@iq(Yhn8@gk92}0<%{h@cZ?}qvMUp4Uxo06i`m+MQ+6(yse!&hq%d>}z(M@ny zYOAKTOvnzE<4?qFSN}sq(k)l)V5=M|h_y5XMp~^+H|%UP*N`Vn3afQDGDo4kyNIXr19A9kQnYzkXF(<3Yv(dR7j z2IiOlntuFxuK!;<8gvozYVp3EN+?K1Hwa98Tr5nJk$~Ee3MT@?p^Xf6w7nKodo9hH zZFGBLX-*T!BpmKIm@g0J4x~4Q@1-D`om}`_G!&I6)d5l^uA7pJEqJr$ z2+T2G3bC({2c4ODJDMkh` z+p2`u;j8(Yn@k&nG85pC7nO|3rtvihXJI#q3v)Jw~*nRH&}-UU?Tl&VCy#tc^xn!sGqwuuty@|jA5jhG;enFutEmR!JqsLNC4Qq=j=SSSO@SlItC6Y;gKxppEgy zfl|u~3WXm!ErM=J4r}l}QyfZ++BHd_)>c*ziId z)nED}VI;$Ze27^DR)bA0IA(`oR)+Fx@EJ5?sOUI{h6(<0(D(lLh2Du@WCtdb(3^m_ zRN0Nh0g};(jr?8ew4^sa;e(p+4&M^;i7rp}t)xjZ{8xIQ7~9`|cPV#> zvFMgokoLl#0D>4H=+%$>ezv%2T05+L@>qb*qTm?4|9C>2y)at@*>H z-63m*p_2GxshIYF%`ZY`DHq;IHZ0wgM~)I~sqj?>fgD)TpTqr<0Z?*#)rn~^Tb}V- z0Tu?In-Ke05K96Q9fh`l5f zT~kQz*{4fyD(c;44>&LzhKZ>%iXV-JKFNASv*l=d+Lz&;{W*&PM-1e$ao-KV83&CbuvkhrE;RHAH zNpPwr;IGojhs+ger$*kSeR}N?r4{IiW?ubSc%GEP4fT};; z8-HMWo7d7^?4g-Dl&rjl_FE#`5ZH`&?=DD}%IW1i2n@td zj3s4&Z5I}dPb~FkbDma7Cd>>0{fON2(L+cZf_Y1P7q0h)q!g#4luhT8Q9TgCjx6)7 zR@1Lq0z8U^mvf`7JOyn;-jMu!u0YiQzrui6>=g1qa*`G`pXBd&F@lG+Lqdzfq9v|e z?j-@&%{6J6V)Ke5tdc1(MfbAt2x*H`=?#TJ!D-xj{{>CbfM)m+Vmo~~^i0lZf)!34 zDpC|RF_!G{2=GtVAwaCI2)i%W_e>E~SL#+CN=L{WBRo}-7tG2GCDCq%D8v*vX( z9kF&K#NW(>kdg0lEXVg#PpVoTfBaYwPaGURqu>4%0ZT;7zvAmHo$^JA6^Zr6x=4f5 zyS?u$35nqIxy4jW`t6ziob`Q59V@#j2|o%;gzNK2UU(URkVphjqt<WNWzMcmjQM($F2X+vppp z^b$LvwHdS^zNsJZl;V{C$u#Gx)(*fm4Ae#PBm* z3tNUnk%|6~uHxr0 z&34GdUc3rXc$IWb;edj^hRR|s^c};SHdWFJ5V$)4GfE)}BB4H?uhI!xvl?sw0!@gb z=13P_LwW~dKcGqslDb2 z(_iGS)|}29iD7g-cUyp{&frq1(l-40@3=KIRKpaDDQ`0a$KDp6KU&hRvPpm{TER)R zt9&mE7@f?DruDN_S!FE}-U)efRJe?g~;W1Gdd%r(u7y>gLn3>Qc<3Se^w$ zFoSWBLWf4P?3>auf7FyEK zM;vJAvc#XN3HDpx)Fu_v6h(#lyo{b|e-UIXbIC2r1f)lybeRzIv^eu717gTu4W^2Mqglo=>}wi%#WBjF@qQ9{mn|lV@iGqM zkuyoqHsu_sbd`nBq}Z(?gmDVdZq=unP3nbo?N%V@i+&HOrI@7=gigyvrF=5R(OgV= zHYy!67$oVB!YO-YEBs;(LoJCM-G*m$HNM_VLdq|ahKAOAor!H*2-1jj8jDq2gjLS za(Ctd_t3^xsYO$>QjdjJgSE@})Y81!t~ffjR@9v!{}!*Y4IkJYk<^y#_`D5WrGXO?Ad?%d)BPg5z;f(&fwA&P~sS2k02 z6g=Mwr`8M+JmeP^d9kd>*rh?Y$T~H$?si>?=#T50$Kok5G6I;v((39GUNnETZY()` z!ckguPn6K8{QuxG<(j58an>#48;&Qcq=v?$ONe%*T3mY{XxIf0w6c8MG-NkZouVQ1 zS9r1Fghcm06;i*1cS}}!&O0S)Qx?B>qnlgI;601m;D){5vF>aP>_x#ENmJGI&LE4T zWvgE9Jy5-&6v0zpWK2JyhoR|z^lb8^7qT}lCha!hHXMk};n&wJ(GtzqxQNp0IiRV_ z;(4GKENyE9GTJBkiyB_E5M9*}ykZdB7+b8Y4eg|;%wur;MciuyFr6#trO$P8Q?=C3@rNnQ1*25lwthhX8u{C(?$F*;0hh-Z;X^Rb8? ziBe}@>V=aYgOLvu-Kfbo+7fAAIGsumC!3(6a&bWt{)*F| zz>iZS^+{Ag<4f3G%!l&!K#xeI_HHdZk*uLt*&16aN&?hapsG*TZHu9$maGD*N~OB%v564$)SDA57RRD}5eJ8IdvEi%C?Gp>!?qBZ2!x!dv< zs3E(a<+UCdWQRp9dbOAB*xOl17+?f;OkI0ieSDzEzh9b$L3z?<+0dOD2~11Eo60^N zQD$R<0NL~`E}_~zdGlDr-u^9b6ZlDX@LnnslNOiQTr3&`=kmO{P6$gs*@l|2986!S z7S5M?Q@TZmCX^Xy)MM9)h{W^SBAd5%zrGqK@N5>|PK8EEZ^^YFXwQdxME#~lSP*hj z2k^GnGCj`}O2*Oi2y+(dU-#H{3E(`)M}hN(OcNB?7R820=)T(&8}I0q&3|5nT$8Gy zKa+#8>;C<7V`fpW7}o>|f#iNt+e;CAGYl6zwx83bLk_m4l>-cYRK4nV^abO29gFDL zkWd6v`{cbVpTI9jjOZFSZdR?|LVFZ`d-rNI3RXPviWT-Q+LK>gu`1PgIOzIa?`sq7 znK$liziYwNEb#n_h1i^=JGesPrhOQ1%`@K&3-K%3TlP;|oc`b_POWag4Y)>&{@4Z$ zQ(OcJlR_8--;oj?2=f|48J3RSdPvQiP1LzgRVm)UwX|TIdkEny>9up6{2&c34-!Vo zK*xmm1g7iIp->L(d{ypK#O$mJ{7|(=RwF`w!>2$zcdy&+B^F*ae*1U|E;YU2zpFW; zah3Zbk{MrN*>T}MXItX7m{X+%)rXW?3&vZn)aGh0?LiW*%B#sC^aLksiJ77u;&xT( z2coWUaL=hz6cEPLJ39k6^NgkU!5NWDBYB+wTc~d5i~|b_Eo(vf^qe-f{eS8XwG zl=@4=;B*_Xg#NsKjAV{XVaMe!EDHnMb}Go(N&KgivbQI-3B{{d{%En!)v6pDa*Ta2 znFP=VeOum2pX^@9Q?dibDFl|wGyzjfzU#%jgQN8MzSW|?lbEg$0?hRHdTy8mbg6XR zr|ZIqcQS$CLJyEu-&y-UDp*v@O zuof~A@C`p`E@VCYt4S!DLk?`cmpV9|p+&7Ns~;Yx$v~Dm-?fgp3=5QPOmT*rDa)$0 z^5$Fy#ZaNur>7SALv$&ev_kRv<#+|~8+Vw`2@wV%9}R7)0>S4}1M*$ZHn4UIg}Ia| z_?o*^Y{Hn3L%Cs~t5=LSg}JdU9KrksxtwQ{P%a#m?wKf15Fio+V|cO(W!V^ee+fz% z+!Ssym`Z)NI`8hOh(6w-#kZ{|%yFq0bP7=}yA`a*@(%1o$*FJ(#k-|l zf>hfUZc1MeAR)T&!SsMA8;&_E>}*XGwKi`p3g*bqrTbam$L)I)>Wg=_59;k%;5!Vm zFnqv?F!!6_%;}y>C5xmD003nG3JWVp3JVh|D9MXS%834}r)66itPcMHfP;#w1)f4e zKr(6+5iaopJI8|&JE#n9e_$iMfhC(6ILB*$5nV%}hDQXWsx zJNl^3>Dk?XYzmY6GvWE`7($b&I{>8^bph*Z*SlZRe+xVVC9P( zwS*He8VI(pGdcL$w&EL`drJ8C{B7R-X(Ij*w5K-n&cSzXY0k1?z10@6yH~u?aDj-f zt-=XA9sq}A_lBN*aB^{>lR{DbWJq;>5560bPiP{66fpZ;2V>{!)UF$OD`HW;bfwLW z87?%087_G8#qWpNRI77%m`C7iJ^d}KkC%UYWRE7K-$(Tl<#n|k#@D6ScS+suu1pAq z^7Ye*$oaRqg}ZOyETlf+Lx4QeCseP%XB;HHz@VVwt#H}r=iQ%{7Af^?25gJ$Wd0F{ z-knWaYtQ)KB2nZe@_x1%%o|KT`YFslcMn$QtN0preZmq1HNPQHCAzs6g$5{o6G9Ar zKT2GrjBjO9Ep)IMet>P~?~c^NTD9Z7{75k$rsszh`%Q@jQDlG?9jM6oe-_Rl05{Ry z>lCKjYybe3>%Y)Zu&{HsbF#3rC;Tsn{8(MrpV;ZDnx)+5)Yv@i##r%8IG(*fCm60=I9HrrX1n-KY{fC!$E0gRX9P1X- zK@lS@iEdAucv%L$)Isz^4L57V&P-sJ>8x4R}bSHAN{P)_DeRxt4pFTkpCIJ@X@E?GezH%7Z9zfXyfYLFnkS>Z_EO? zd+u9hMFb_JI@5mdSAQ;uI3|-M9IIzx8lRqKKXTL<*$ezC_&&YicvyOl9zOk|MBL0t z>PIVuJ=yDQm{(p1@%A)>Od2lu`RNh;A)W7TGUKnmam+asXQRsTVssqB2 zBYu4MrHuM*xSH3@xk9Xn!v|tn%@mK54|=e|=U<2fg5o654E8z4;pfeX)YlEY6fxZK zO?$H4G+??%mX!7KYnqNUdK%@f3FCQa1|=T^L;{pr0YEJPb^=ObfMD&g zhy;#T<~C59!NC0-?(Y7AGPrTAmMZK0SVOS!xSvlfhvf2;6&KFM6fiM2CwC(35H zt+2k(wpd)zwgDp+ju@12(CvtbVIq?iV@djFy4oL)KZIuREK$Qo==G2qP}YR05$Fa{ z^!pxYaBb3mhL3?6`itLIwlxdXvD)6ddXTq|jsS15XPs-*drY3Zz zgQiX{DT>QZz+5`I`kjY09Zi3HxIl3gQdvl?#M-6Nsf0^_eu7v~T_Q$c9hJ7RjEZHKZfcvNas_Vbtc;VHLHCoFyvc zfohtff2Ds3d(_>D9sauUD~qVrTZA67&saKXH$!rz>I$D{%@>mw zyO)A3T`WH>@6Wi-CYRtAwU-EhNUT^mFgWNqn>Z|)m6;%!ubESs&n-7v@HM10yjne4 zPHS9i$ZOio+|3j&2QJ>ucg~qE_-BbOInL_m%jaA3gC|T6N)J!xo3mk$H4jM-Ru53{ z24GfTwZDCX1cjRmYKUg)x9nHLO%9BbO&6_MEoE4qIOrL#7<~;3fhabO3&kr3Nfjx; zN?_0_7z;LweiLXDebe^TCDAmk7TW6TkF8}k+kZ9JVJ_rWKh5k+@{IfzeH|;N zm8R!ION7%cF%bh^LqEVe!zcHd@_7x zef{&Y_EP(Jc}wyd@}~OEd%N;{{O$(P57G)!!bV-9ta|@FW9S!CPnGtOlPZp0B$rP!L z>BcX?8jd-O9uW%@=MbBzz0qj!8g`FXl~MeYlfp&gq^74^(oXHNl%X-MVRkXj>8vBN zG1rdxN5G8NiG7R0SN{WkGNvW$G5hjQt%1A)F6Uu(1bA5RUGTU_*pP07vZS13isXV0 zhb`N)X``E?&7F>v!NhKI$YS{7GOLc3&KgxNJVVGzQf*>x5?{Juykas_zzCnBW+ypMKPgbd(J4Jyb@@>IIIwp#u$5 zdRbXfq>(S8gW4Bui?*URhqjm7W(_vit@6VcdsbwnWGQ3>Qko?zy-e?=XIUYaFzfG= z)sqMp{TDlz?M(~7;{?akyY9ONU68IdniKS4^g6fEt6nXjre;@Iy?DEM@kC#xQ{B-P zo)^Cgh3a;#Lk)1Vtl4h8x8w8TQy3~6v3ZetC3Ufve~j0?ZMv$qN3W~*4(Ui?xuERF z?vI{r=3X016>D1HhkLr4`+>w*LN0k$X=#y^_+;j;nc@9nL%K0XuhVyJ({Oo7ZOXEi zBQ7o`zTcD2#7OHAlfB#?43Arr@0!oY1lI21z8W7AXNrf$C8Ll0=bsx~ zM!b$@i$2ZQk|#+}#?=kSeI{RbhTS(*ufW6LGm(Yj*Mn24nTnq?YCJ#va&NtTJ5SV4 z^`CoYeQpQsru!bJM5d$r4*GOndLB{+ss`)m^xbA4r}%u6p3Hn}Uup){Nv^T3J-5ZX z^U`O&{67^Q6|ap~DEj0PG3PNbz97KT??1@gFkJq1_(2*;i;Doh{~P%|Wl8@eF!mCf zP5=PHz<&gSPATR8PYC5KDJu%K4~m3|h5K6D8V>*<<4B4Ks{HHz%K*BoEN%AtJ#Tfm zkvAQXqc);63ap0ggof`#mZ&tOESJ1BFKcOYL@u{v*@${mFGDn=Zy9a#*_157LZpF4 zVwkj82S`B2$1|Gb$s}qrI~{LzJogenj)qxEdrS3o=X|GScpd%eIR5xL|0TY|+GsqA zz>SNr?@06ST*`QH;zDwk@B&GppMujl4ULYTfeWQDA3ssF8V06$u*kWz|K-z8cVl5ngiWHvSQk-Is3xu0!aVGoW6v- zYdshQ0n|@~z56!>)f5(@Kw=|A9Bk-+>>MLOfpp>6$t|{ZCYLAWy6Ut;r|+=L`)Ydb z>69>BI7fsQ+4MOzfzfX3XGFH)EqZODkUuQg_?7Y9!*yRKjPu8(Cy0Pp7batx!V#h<6}xns>h2i|?+%F4n@>K0YZJ3|8~bEQWoJgd}qCg92wI$#HYvGlE=86>G9p@A^*;!DIJepo`0HJ)CUzO z^dG9t?!8e7HjrTDGi*(*nDh7h34K6#04FCM_9$->i-174cvA4Ppc8>;Zk$vUm@p=p z9JU!;q%RvdjV`|*=6O!+zjKiQk(a_z=NZQa^5OQJGCFay{IjxttP`nZd_GW zmONXsw6lo@9hUuN`SzKNL}R)scri#x30PO2fP)$F_&?222t%t)R@@L@LaiUYHe(g) zs}EmZtr#mT96BnloBVjr+M{_9QN+g> za=%J^j*6{LPA>~BG$c!>o{wmpn_cUNzKLiEwr?3I_|Bgc+57LBuEZW+9oi3MX5S?) z+(Cc)%s(qjS}TJt^v|=3q@C~YY%PT}jm2SIz?fnX#E7!=ekAC3ZNRSR+w*$`y5L@Y zaE}hU42dG=t)z`Z^3}x+6bAme=UgdqzaVD6^oo zv}REZ-?+IYwIr=K2NX3_`WyCTNu#0heydHuRxuxCv7#(q^GjGWhHl?AwY?(gDseA_ z>i&*++P84XnJW@EH~KAp1z?+PfbS%~AaIa%b zPJVgSwz|AJGJ{_id*Y<3tssK3IEe|wFB{>H7O|oci-pSLz++8XrR)?Xt|)?)s~>EK zU1*6kUhj>OXpEUrjc1Sf4UQ_FC(ns1rmC9j?uk4-50d(6^5Z{0d0K`eTGDDY_%Gw( zoUhc)XbAobrV-dD@N-V_4y;AxbC{bsWNTBEzb2pofY>j>v(xGy|mBqVXE*R{~D{^BE z0Fn*29zGevRh3e#b2A3aL)zEfqOwcwl~q+hAh4jalRD|DxjlB~e6o1t-)0>xc?UI= zC_8kd{Mq9U1OfdmR-55KQ(5^Bk;*G8iS73|5$V1s_~|xraB$phua;U5P}==QiN9V3 zlmJ&Z-l%h~^-iX7H?PJi)~`NU^|vci17n$%N6$SYHDPIrZP#Gkri0-vqDW5N#WFGO zq@i?C5~9ArqSC$wqb+f4Yz0TSuso*N(EmVGv+W!ajkAhUaYR{BPt;8vvCZdRJ3|0F zM)Z^=t3ss=DMs`k&dU7<6}qi0ZHg=1*#3{r(AGE zD>=1P|IECI`}iz^sHl{Zw^JX<{%(BqgLdXnX0J}n zZQr=uca|H$F2G#T@Xuqy)2|D*)Qyu9lbY;%YWCzrL^l!q+it)bAL2#fc}B`{f)VH$ z`>RYhJcb(GsXwQf%%2qe?`aGdns?1~=?JtWp@h`-%+G5HoPor5A8TxY4Yq4fleDC? zo-z-x0W?a+d;s|SA`*^F`+`;1O?-Q0Be(ypQ z>oZFVAeKd_oVp)FXlYKg1*mZqwH$w;tzi~dG43gs?!v4Qj#@07pb|i$xut+?p~RXx zh0y(l(XK{El9ub{bSab2q&d#k@Eqg*ot(wYv6K1-uI=8XA|CGQ(_aq5JT2JCLWzbm z3eyZy2}5b}RrbUSoca9LlCp&uk{)3vEsVqpV`(arG&m~$yqarkgNFKy8WIwBI5-fE z?}^HSskMZGYf^To~S2!unu2ND`?sMFTznPJjbgJ#af0q!xY^*k*aVjHa_NK9v+_@w|*&mjP-*17yvW@;AAzQ<)QKW!zwb>wS2+2^@CLP zN;{53$5n(;#h%8=biRA~BjBU=91LUWHl)Bt_f4rap-rsP0Bu)hzo#EBPCTT#Kx55z zN9?)c(6c9lV))!=f}Oz}FL&z$Joi#nW}`LMyNO9jXSaoBQ#i!0%dm7I_-xo%4O~T8 zf(lI^CXu?VALm<#b;oBPcWWt}rr2=qgx^)PY}*GS;sB}N_LO6#2(7-6b6jY%0mVc` z8fz5l$E1o8BJL*|u%2V)Xi6k{jBMdd2 zpB`pc2=;P$qYMyf^;rHjem@&!A^d~VP;^Xx<$}JdoWcaQ)zs-*0g67_wV9$@X?G{B z=pymhn_&Vu^Upoq@C3>q*VX&setv6_cWgWe$l7SgI`q}OSb;OcbBKtFY=yMEdA+{Y ze?^e8^MR|G!3N_J62j3Sg^55)H+M-K$VN(~167u?MnL6y7%=mollQduiw|gEGeF=jY2kock-c zpHOshRNV~k6BdA_R6jcYoIE6eka{&UG{3#R2nRtNHtb@^aD6BTlwc80hM^yVoLuY~ zusdB#q#-~YdZ9F%0~)c6@o~>^)WPr>`1Rz9?{_V3aH^3R?tTYv`${4|_h86dVmjzR zbYpz!@(;aUUlR_|9x$BNrTYv$Y@jqSCXeGMG-PE^-ClPH8zU{sRw&#SS@mWIGmV8!Mw7R=&Nt%xT1SAKQW0;99FP!i>hXDQIfO#DrE@L3h6XZS3E#MdaLA4abUTe0S?B;=n4r709fn z^Wj-VIrTaF`f>x*t{;(eaHMe`(0J@xO?DVPQv14XL+bfGAtCG@1c)jX<@e9%(?=%_ z2_-^MlgElBfQPE9vbbJoWYc3-y!kW}@c5gasKnupA88T?8Y=R32VRTnm3mBWwI4?UC(!aYGKNKGw#endhDdp)z|*Y4CSB3=>IRK>QoJ(5oe z;>qX=;kFuSSmg~Ri1iB_AP%zUlv-q6z*)vC5AoGW6)3I?^OmPV&_~dJJtvf!I(5r1 zn%u>}N9AZLK66#$ZNWy>sQ&`NO&mxcNA*~3r?dg-4Dn_z#}Z3~QMN>ui6Hp3eSF3N z4@qo@pX*I9dF`1(rp_CcS?1_x^~3Fwy0E=H*j*w>~eX|v}1{d5Ww?h}N z4wn_Aa%GjK+P}ABV+{kH6*;|03=pr#Ura`wUAKq%>{g6QM#RCAZMOngTzzz4;i zI$m?#xO0Kj92iBUT8{0G8N4xG6hx2Rh={!VG^0~pv2_EFXl9mT;tUDAB z&#x&Jz{RsN#~#b!Y3jrl$u3N?DSTX#J*7`AfyXfwV6>~6p$eHs4wAgj_}E~ozbNSF zhTGOh{7LYFB16#?sML%`*x092!wHt$S>5SP$C=lv-op_P9N&9U(AuITP6-pepy?Tr z&P`oRS=tEjey=vL)mQ(LH52`5r*E`#?}-&H+7K-WE_wi5aeZQEPdtCTANlgB&OzRf zh&1NZ#gD8M1K6)a@p*gbX{~d+@4RTz?kS1HMW@L4=hsVgM!z;tC6_~}sKyOBQ*)|t zfb|4{+mu@YcLN)Y7pPW=_MEC#JBM|9P+xkDQQJoJin^MzfC~)yEln{tU9MpEuPN$E zY=*cUzA2K&IpT(~&kyuB@J(ZM(#N0De=>oZS4jq?%jO&KY9j*R&?+u zJ0iay`x2fM)1qS0AB!{B9Xxe6a-LCAfkNs&ok&S?dOUc4rI;&-q)xGnGR%&+`<+T7 z8m0!QCTTge0PcoSf)PjlcDezCC071;>#iu(m&CQA9_>EFKS#7KdVkOK%vDHq1{;>l zFnkO{{JUC3_eb)484M6_QEtDh!avfaF$Dgfqu(#+gCO9sy59efFxbDu2|!X*PNY`I HAn<o$tgzCsc2hqzgm+q|6M^j{-8aokgOZ~lqlUjt3U>C5Rn8_Ej zf8TE}er`ZjJe z^Q|7%me|X^Z^`>M&YyP7`dM=~&doAE@b7$0;^W<}K0fy5TD#razUH{O5TntkY?&$O zN!QD5aWI-RjY8`s|1#xP`ndF9No%`XA5M?2I`(|}H`mJfe(u-uzc{@QqbhOaKQITQ z73f&i|HDh+Bt4(pm3NqReIptQStUvxeAVviZr%J$vFq~ix`Cxhd;RD4?da!x|9gDt zmFJbJ@n*5xVyG*aKgx2%>2pelb8tIavVLWycT?Kt;p=EU6tq6IrM~BW!aX$gBW^AF z=ycTcMe-_XruS+FkQwS^PP|7#i2K9nMJf)t>~*zvH>-a_Si*4cKg(%-OV<-_ECfe; zw#}o|y^ZbYP7gxMK{Sd?T|*dPuPT-PR$h)IOgx&UJ=1=ap>^Ytq^>w#oTM47P&krd zSyQ~SVOd)^oT6!4yu6}mR~U*HP`4#rThqGpiP7cE@U(Z{k83o=a45q(%XDeoI9m^y zo2;s9vb?OSXB3Lde%?D-W81p@%JG9JMV{?FQC^zuJ3_i*+jV}TW!ruI!rjyTa(_{) z``GJoQ`>#QubViUu&w6j9zEIY3%3tOuE95GMCiezdkh53HD)IFND1a4%je}uK`m#GlOg36aGt7H2bb>EV;V#f+604b?35hJHm;eDp4Y17 zfvt?oO6EOq&eZL9J53J9WW6h&Ds9zDA~t`$WVO%I;w|kO_Mm-zjyrb$aPaG}`T3qP zI)533`|P{2rU~+e< z%w_sR$a04>Hrv>Gkk%gybSzRM+up&7_uxvJDufd`_V)AHbxJSy1^uKFIYW%z z@29sD%Hme%3GKoXcb_>c)iI1Os+fOMT(L5>oGFtykV(?$v$!V_?{5?!*NnF@D(tRsIOzY?dm*SH(3+cQGWZ!mx%$AYYmd|qGmW1PKC zc;|pj4>ziRp1f@($te)^ks>!M%>_I|DVd*?4WNO+6kR^3f~+w8f_>JFIPD@jSuKsU>0O^X)tAcuCK^_v{OS z6T0kEZpgZ9t3d#~h=`9BdHq3CMzOxCZ36&D8yAHiq|6_0huTK=))$csRfn`DcR7?WG%5Oe~S&-dIWk;&5xTG)N$#N~vHW@B`YDF#`tO;V@t7sgM>X12@C%V?-lqrHf()DjR z8UmExz|n+igsyI=Rz3>|txpT@3TSZgc@`?B-GfH;EvErvXoS~^sR#M>O04H}v(vsA z6ZptsLOqma_Jvy_#3qZA^8^kXM1|{o)t!e@Iv+`(T5waw zg^q!ZBs`=r8}6GY%zS`uz4o|KMuyk&6@jG|Qc_7b5h`ON-+oFw$r13?o`i__0Y=Oa zqGp?-3J8Yty%r%QeAc(nm7kfsjjj?WtR@P83%d%8t|_2fMcg0Pvhzn~h&SFhD-I_h zvXr}>o6Kxq>Vp|T4PpcL&6*&3YgPBs@Yh}P(+L-If%Fk)ZZj@Sso~5)H&2S=zz`g;wnV$$}7usRlk&5>q2jqWn=7fCDW+ zfnC#b2n-EBQ)gca>H|x!=N9sEQ}fB7tXje(iCnpGy*L}nW7-A`q0|d4L&%ju9zg|2 zdiXc80~prg~Nhlvt&q+SYstAnXUx~_Y#fva6 z)_Xh)onTkjA5g94LR2UU31Y&k+jQ`UP~-fp`hQQls~}ua8*bkM!T5h+HTa>`H^$b+ zx#(Z+4qfrXXuQ5Me$EQ5eEUrOG{(93;Wz%$7;oo}T!Ap=yDN(B)xn$+>4bFM<*S5& zmgT7qSV5WlSFOO7{yAb--dd$(j;g;9 zrMWRqQ-akxCMV*_4@T-ngs3`r5fUptC9Uj=`FPB;{Np&_u;~*H+0wiBK;Dab-9;~e zqP`P480K-JlW(syv4jIGoOwpLI?TIpS9hcs;fPY0q>TV23jSEUS>`vH=Mp)Vj2+yD`3=o zL?C*_8R+QGLR0zzIKEjqn~7znHv5bB^ZYE1Gcjq|XGu{O35SXv9I!c{d`<752O?Oj z=?BDeb>e9X-9eFp)hz29m5CgH1ApT5<*%KMs(^mRVBAXb$yl?wYkW)g2W_>0e@y=8 z!SUuhjiSv~vK=>z3oi_h00D~O_{Qnr!iR=sH8ON@DMAsU+TuIPg@L947#nO%7cE(= zmq2UM7@6Xl%Dp_o2Cs8D%3L#E`yBEe3YjfhTIl#io17h0REDBE(__Ba?#dYQR*+LV z^&eGU$`&<|75a4iDGxCeZ$u>!P-#1H+~J0=F@Zo;8E-HKm8vWifJjLTNp<%9_Y5+hAp*r(S3sh)P0`0D2ZLXf{TD@2ALIg zKIigFtMlXH1~;OAg`+AhJt_M^eq0i#KGTJN7-r|Ijv$8YkHMbRkd^=i8PNV8_q2Nj zQf1iuO2I9xpor)qA+(n>F_PW#VYH-TF)4v1HFOQ=YBfb%+TH@-qMP!Qzx{Nt3NnB6 z?ej*@%5J2Y0|>P{RXeJ6?pp$4rtL+&ZOktoZP>;UcOexv!-{M+kps&r#I`Cj90e-$ z7{J&3Fb#r2`O0n};xIQ2?L|%-!G0rfO7;NW{g;%Aa`)s4Vysq~Pq`HmdhFS|wiS#A zPf*aE`6gM!(0`WZ?n>6aJCpoT#KAi31dxvfH9-s!Ny#G59u;)D%P@_C*(%((ix2u^>FGW(QvViuR@lj_ z6~%@XXJDDFw!jZhO-RF3&A~n#6E1vg{u6Ko2S{3Nk034Ih$?AYND!eP{#|CJD?$oy zUYDChklnKEt;8P4+F+aT#Tsj)nqdQqkycbkm7>dtARk-ZoRm) z6%qr@hrPd&uIq{rX8ovCiEKns&=v#P7U4$XGSL*C?f`ZVG9PUGS?;rpViw34{lO{2 zTS9L49eo)0L~J?80d?ocT+|=MxmZ-u@Y-D$_PTFgya8jBnWs^DIldOpA%wO{ZUZNy z>`=L%pW->dV{mmik_=3Y07P*WHfyn?RT~dObMY)8aZ5khL1+zX6$f}b^K9+kgf7A8 zWEMd(01i2biniX^@^|r|(PF3u4gGx_gd#w|O1)cGb;lSTm2~snWpmGbr$w}kzgvj% z(cw@+mB*ug1|y6))PGfdRZQ{A*xhL=$MvzPWxsLrAbei5|}O{Dij_Y_S` zz-o^cbejlr+VX1z=|C6@PO;TZ{oSrLx**8CH!D4{*sU*}*vpCQ>8CJjSu=1Hq3o~Z z$nG2%xEgNITlhi+NKaS{0nVdhDoDGG^ASO2BZ{+GRg*a22PoVUX8 zQF1?WKI+7vUVq^XU(;LiH*=Z&c|nN)1eZZZ1k})c7#fn=J-P#gaEYtA^Q)Ai0%Ely zD;01hL1?vMspFF6Y6DV_nemWQq#WWNu2b0MfVnw(d7y6Yv5Hj-{-a$&zUY<0BDV z5J#2xaA068tTl>8qe|LeNJdoa!vKf*Q|9vu)`q*OOYFP85JI6qEp1kJv0`2y{VqSf2rx$NZ-sGA0Wq}@KEpD?GmVFfqAfveC zNW#Af?)qD*QB-(RqS1Sz=D_ce7vV5LXl*N<^zRue{*;(qZ#G}CM1PKvY4|tt%2JTB z@;Wo_o0FBwqTq%pzIzOjf6RPuXsN3K@31r+ee2GeiVc29tgi_=@}vk?*SVi3t*8Pm z!bp^<9{lK8-ZkHbLfA;0P}@NQ`VoRzCvSRzzrO^Px?%bZ`5nN~|72=uoml z33`=bc)gO64e^oyMhp!P&3!Avf|3ccJ!1InP)kEhdHg0f1^$jMS~Ux@uyuMu?t5qm z7(GYh(WbKZ17f+n=YDR4=F203)LiRB&>3|zC>Y1(y48Nx$7NJYCzZ7p)k`hk!p5Hi z(q~Uj+neyx@yYx|cG|HkX}roQK@CDjofO0t%8T`RWphbMZ@lPeBv(`m_#!#c+c>rl z+5y%<$VYw8N78{?w~01as9LX)*|(3WG9&i!EG>W}#3uRXQvYM<;B$qpg4fY1+!ER& z|H{7*K_5bHYiJtLDcn*6mPgHM!%iw@Axl@tCsIJSjZ!|Athfo&dCfFNQ%OMLX#p(> zxvT-6);)~aUlusfTds_bjL2A2#qB9Ns%9kLla^#}Vtdc7k_r8&V#B~vDnSq8Bz=`k zruK!TpV}I{JXWiTsrjEIU~s*Od2FkYd~?NR@c^&m(`GX)~UkFOjDf ziIw$N zu~__SKty?H2;H2e*_|Rn1Qz4pg?I(6t<%cccURxkRafM7?Oj@0@Oq?} z5t^`S)CpZ504LXiFye!wO&!t}5zQGfO?%ai%_h^LK_HaA?s7A=3+vJe$9p9$z8M1-NjecFe@&_Gx2hk2SgN)cB+;v1ebdRfc|qGbf- z@k_KO)=0nk>>EqGa)hV5QMr})B?!YYuQFm3|Mt_%rgBn6+|uoh3?Z~yS=y3-JIEwM zPWFu#GVnjj{l}XdO5FS$`>c`}F9jE3a3aY_243Y5jpl$_w#Bm@S9bkQPysXCfg+xC zrhoX43MfD3r8~~coyirL3hn+v4)6_sjuyK@`*Q77>7>)27>PS1y@S&rI4(2Yh$z)58XsK8v3wSm29AMx>UE5C|9}=OF(v^WiO8jxQdr4Q8@>kQ* z5Onm!38430@H%ZLNr)7~mO3uo`&-(&ZgL#}?3Tn|rrhg*eiJ=8ZT*Q|eByw$bRk#|wQ&yS_ixX*R3oDC+ z`sKHZ3OM2Z$#k62`QpZs>l>qYj~BVX~q+i102EBK_yYrciGkRO{2H5o^x#UU`9oA=D`-Qyz)uM3zbHc?q5?sl)hVkr< z_R1DCXqM;0)tMs`P$m;|pk%KTo8eW6*XP$48xc-a7OO^l3+NR9Xu0e68A zUJG(6NC+N#k%3HC8Zj@my4`C1ow|kwJFEHv&qRGT(^ZYMTCl}W@7<-sF@+t=5Mh9x zfbqmGgNVMcPj5h(IlY?exc=f8mdAFXl1|{G&#e5p+{3DpAga-v7dmY^OS!%~^Uekf z^iz3Pb<$YS@gcid8IxODr>*qfg!KJrs<8b>-g2p*Q>j^{iLDk1tp3N>=t0*fetMdc z1s7Z}Sd>WoQbgS7*YU2pK@W$Ca6I9K3W5ZLQjTZMqyKZgCdp1DWi<%xQtmGfWeVuu z@qKf@>NN`p7woxqu6N;VUX*@KuK-6+Tr86sc_<8!dx+w@;9*eXvY@|D?I;iMhCmC=%)v9^&}d>^`&R z!FRJ|@o7qfC&PSWl6CG~djOXn-rnw$5}{|Sr}xTEb%+c>`Z4RXG~vD>^4Gn>BvL0% zHc#cP?zY#AyjB-`>pBDH7TV8-%ZRFZSS@bUegt$hlNr5mW>-ec2IT_X#^6tm%zQgF zQ$blU?xyd{0R=QeX(E>n;r)8%M=-pJQTYj?Q;SP+%B*jR%5gq=1_M(7d#K}y9Q6!@ zOEvQud|CWmo>Wdns5zL`2yw;o{`ub7fq_s}cX|Oqmm) zWv8;k8fM-4^yDzPt378_ZSgD>q@5jUM2Gpx4)|WG2u-s41<%>Hv%cDwZ=)CaRGI5` z^E*Je09{qZhcL+{iAM8Rs9?hfwck_(1~RqYwdZq50c<8n$G!#rqU8dh@!d5AWFRn9&m@m#`%WJRv{*7JNi z-LsL6+^B+7fOZ>HJ(Y)zhTM4IwzM?B7SV?&cz_DA&NkKs=^8XTJi5HG2!4;k9^M}1 zP8%bt7oCI}rc6kaTl9WBwEB9X;o9G9xj+0>h5?$zD^&%qDJ@UOr~%pUm|#KYrc+)f z#l@M=n$?bH9%LA2qXwoW5kfD&fzu|PNY*j&MYW19h)zl@a}Y~YV_;uOZyc$;?3)m( zX?a@la2`u;hAklawodUW?PKgw-1IcwIoR&AF$TdHB)Q;H3^Esfi`iS)M*AB9B#rvQ zeb85K#bJ} z=W6--ntPMG^OhU%!#w8){=v_OI1D_N=lMlXPEs{XQEfNyACJavAuKE}DJ)DNuP7%b zDI@wHZI*RCEC>f6fCLDzMPN?zB-Nz{Y4icRpcRH@9=Pb27KHC-@)O|6}6-0EzJI6gOq%RrJu=Za2^TX$DkE;oELFf^PQm1rdWZ zOnnLvcxIM*!UA9B8CZyS&|iWN z&-O_s9_5V)(x2UJh#J5i-4%4^eD^Dvk=*|$YymWUEpU3=IJ`}MsC+#Tw%$iq`LPBtSgq*6feX30U!@G5dmZv6oVk%7EK{mg%n6_3&+1QF{%gTz_ z$%v^Q+C{JJ&);ybPKnw;{#V$7XP^4-WIaz_K$M1pjjMP4@GV%L5i{W7wRepL0hoaD zLhG|n?KMCAlvI*ntd5y+d}fC2#8G|ZAn+&u_xz6iY56s3`22?gVLLmq548mP?4YB5 zL1{6>+tU<0aX26Q+av7PVNWl2kRdp+Z{NwiAk2O~$-yJM!1ZxW8;Cty{Pf{R3FX&d zEw_ndl}G`L56H5LF%Bya_-K{SzW@;k*-4-Yo3KqUZj2289EZ|%=(502OO2Q-iY5lj^bT>{v` zAE7~zZw)d+0BQ}$HONvP21@|N9Ee;1f&-4ue{zqj4ahbKLLW3L=u{uP3+NFM)PRsl zBmk8_MFNJ&e~J+N2+~8~M;;OtY*;WH5dm4~eU7XQlq$3$pQ{Y&1CI+#$G=7>XpYw$ zH6zeBbY~D!Ca}DYsR}N$4|WagC1`Lj*`BBiYbFS0|Joi+8v#FPb06l;<&BL8m_O7E z$sZ93tKc67A?y$wlt@85XmD(ma74UR5uSx$N1W~v8B?GP@vS@tDwwD^X8x7JPT4U9 zx+SY6ZUw4JG*_Ud_;a3ZftUhwnVdP3CX|hTi|}V2@qGB1wKGx&@^+Z5u%6JaSZv|0 zJ_81p7=%&K{fLM`0;3f}aoTs9>XgS6fhjC=Lpf^z3lzKK7l?8_*YrZw|rV zIYJJ^0m$*-=wK=#n0@4Z!hN)O31#vPV1sK%6jG@*3q5CDFn7>whvZ1r7QD`xEhQ~=EeBh=SbkeR zUT|GZF2gKoE#q4tvS46AVWDAdV=-q`WB_HnXG~|jHs5K&R+CioYIbWnZE$TMZD=)d zH<7y>xp+H2IA^%vT_m_?7 zH3B!%HSSJ7Pivg8p0v`8q357q(sI%2YGSE7(jchsG#99YsoT_Fw0xM`7)`g@*5@?L zH-$H@n=w!2F$H22M^U$!YpHEMx0KrKu4`;UUCFL}o7x%Y8u~5yI+o8U&Mf>ZHiEx^ zmkH$xMH25Y$~qJrMIvV;}dMy#S?&`((sG#zE0W_GS^L zA>k`wM?uQM_GEsR$*@}2PzN#Tk_waR7ApO3G?+bPR6U3$GYsIQk-L zL@ZRCU2L}IPQBi1*gZ-`Mqw&DnTy&w2n2U(NxC<2WlEG&J}jcw8iONGDuLQdTlqa#5Szmi5)7!OhX; zL0d|HVm~QlDQszlMO#yQgE9w}K4dkqCLt%0FU=rMA&IeJ!}j@Rnc17)*N7~sovBet z0PS1m8OkLjGo(->MuSzuu5qwY?nL7B&hf+X*{Std_Q>PJrxU#SUvmk!9<86z&zWXw zv&&_k%l&1?s#O)eHh)jQ)nI&da5N>h9~A>F1-*MWrJ<5qzT^JCf%<9P%*;rVh&Rze zt*h20TTz>1+v{D^dYju8x#6n=E7B6uWKw)7jpEfF#?O+A%#drS&CkiIN%*V2tG(;C z#zl+Mc*paHu7`RZpw12IGqho}TDQ@g9!(1kjn2?I@iw!fiQWpQ+LIkzFMefmmE9VL zYK!gCCcDj^_V252VTde*rX{M?l%*d2F<$r9naY}O-Oio|#1r|I{8G4mxb9u19ve$# zYZ{9$_cS;6BZ;y29J0)kl0qr*$&7tdgU6-%G$ZyNr{9{!;j-eI- zuWqt$*=Ky?9P1B3D+r$(6gI=x@?N%|GUAG(5@yP$dz8CM+d3~gHvCw>?r%*S=NZfm zW)m~WGw3-w9I0-$FWw{X=P==aMmmf-@wKD6+$+i3N`Dh}i>Xt2>GiH$&UxL2_b1LP zUDf1tjI=M?Y<;@BXDe#2$t!a=KB_-AA*{iVB)n;_HcX9KkrO_YP_D}Tf2w(s((pbC>$FVk3REXes6IZ@;aI>`83^1 zo+UyURn?#N8vi^Pblp+DTO0>ph%6SpADvUqmSfMV@?iVr+`#}+z;B# z^gd6E%tZAb^=iL$KP3-T4%X7@xy^!4^Z6#enEKYdRS#?u-(uW)?uvKirp^BNf6G5B z+#0Qt_sSvsSwKhs0Ru^U{33NjclmFn1UHlx7XkeKPx87;6aR}q*-L0R0RZs-t*P(> zKxh*dUEgEr+Kl_8@*gbx_i!a^YEVP0&dp3qU!4s&;6`Z zw|U-~KmKPYLEcm@TvYLXdHK!#5{C3KjG?5N1=$g4qZ^^k9Ci~|ojxp1?alOm?r!;V zhEkj3pZIs){Z#x!08W!NgQu?r6;zdqexmjfB;X%c;UsW)+rI3LW_m-}0q+ul0Dl7w zj1*Qfz)kP_c;dxud(D~jIky-VA}sj(MDXq0o>%H22t+*#3TymOw#n%Ji$`T0EEIJ* z@bHgqU2SZ!omxUN3Kj}v?9MwAV6`@z{p5ofSmFYz0?XCu+#~Yrvrm>PVl#wzGE_^& z#x02iG&SfN@hQx4GEL+Oh9_abjIfEqeF#bZ`V5 zoH&Stgj|&;0Cl2?nF$d$%;9X$w?T;0`}gwl-=GL`G zBwbsmK7#}Y4tQQ$TiVHqsp|^wOjBUv+he7xT;2K7Ez(n{J?!unKN${nj=&x4@_{n) zWL)1IqY9dW!oSgST--KC;j;Z2gKyei_s62dY`4K7#??O|K-r%@q1F-g6#pf=<;D~+ zu+I=NI?g`Zoh`hrlI0-=Mk!2AO^zU*g3v_=XmXDdv4gm8QT^E5wAptbKvz>)YB2>Z z4ck}?%YaQ|;Wmyr6h#iH* zB>d!tP@taf%*K+Zh2F3#x>F?C7O;^+VeUW{(jdvgn%~*|=Ht2`Ww^g2gDe@Hw+Boe zm7v6ev-%%o%7mM^3i9va=CM%)eao|(ACc;EKPU=}94QX`VZwHm&mJG!wfgX|nRm3V zIBoLbNg}TSu##X z;EGD>T7O^%bzY2XHmzkiYiqa=c&FuRe{$^LE^FWGy)wD22x+3Q@+{wz(TiD9SOwgp zNMl}w5xeW?W0u8J)Cd45QTvi*?v-zWYC86jUwHLC+hJ;}t4$mqp?=bTuzwc^qJPrA zZr+K#-)#AvYN_y>d*uBtp?A2I2V-Co%QO=zXLMv!TJ14}R0eHR=-P%gJ$8`^cRirz z1$Y5gc3(j*iye_*{%EQEBDwPdlJ@BH>PZC-qD@ULb&b9#;{0VG1cHpYu4f*%_Ho4zSN&NpH9 zKj)9~hgieI!_LpnYaXOENUKE3KMtHBqM|%pKbw0}LP7$OL;QukPw#*D>JhEVrl5qo zf08H7U@v<=)CbF~e1{XZX3t=9@tiM*kG+>RMHN@aXQxcN+;CcHG^bFwTNuq=Jf?cg zmL)|yR`~t(IT)TX0?&f;H>^YtAZ6pDxm!T&CkyO|sOfUn>f!vcd!K3j<+5)15d>C0 zAOx7#F}S9VYo)KOc}BX@nb*ADAoV^kyiI@J%=F6IKV-<=y9$$wY6>r~aKCxaD!7)I zpk!!i@!rET!cENM9M^n0DhfuFWm*FXIDsUZn)1l5RoRlI=6s$g=&jdV{4^?1Y^wi3quYZL?J0lb%#5q;&Gv-b=5OD!IQ#sXdh8x5BMEt+ z0X|>1hK6>JJ)xV06M%z3y0pw17^b(`+4-FZK{~|lpp&1nX zZ_urKXzV#I?9NNw1WT%yjD(-K;!RYa+`NhM=ph}d`|#@ho?GkI@PuK6D|TLzUI;I@ z{4uF;$0bUkAfkXa)r0M?wZHmCpup8LpDnG$mu5o*{ZmGjLPeOyguFoE+`Y`_RZH2MEsoANbQOxfzwUy6xAhO}OAud@ zbzI+$KfVUf83uHIoWA=ry;W;OD*fs86=h=TU>y5545L1?c2cz#d@peQ4-qI&(HxQp zc}fKl2U=BG#9;gYVW+)1x%g1(_O!b zhV|F6>&eU~)+f*MRI*g41g8M92E!|K3HYPB_qbZS^T|5SL-YK~#>i$o7X0&aT>ZVq zO<7NFsje?#D9xd3=Xed)QERE2;?4~rGk56s3|r0J)DSTj2YbeF|n_I%c zRH!0`11d2^a;yD2^=xa}@LRq9UO#dRx?de^Kl z0pcFnyCPnfEsEZHOY(H<&C!34x`+6WjwXRhLxb?gQ8$Z(EP+Oz$B0Q*QeWEwQ>j#T zxR|2Ia;NZl%s@t`(0TyWbruE5IkCZEcnjSlC4*Q+-2rZKsjdKNjzvDND2 z@Y6a01eX^SI%?jt3le;2`%5;W7gtU`N1nX8CS)h}o6$JIE5wWaW08fjXUW~7l52co zB?`QFI2L%Iss9N<8j27j7?F>Tu_uiE+qU=NuwYiA7@tW*v^cf~?Tx9=Ih^(PvMsFs z@ub^cT$T){4Y=F+=_X!Y`k1h!=xU-&u1bu)#Mjg_xSo%!83FQ1&7!TzBCTBG&x8vo zbKuqvOSl&NNkVF>98Z3NW0*wAFzcz#X-HqR}vX>#(t`AjIu#^;qH z4folNO{0-YqWX@`jLLHQSJ$sK{ErJghsh4NqHtSh>{bIZ(?cZUmLU8pYXKG(M>ern zpQQsoHhPsXe8}2I_p!BO&qQ0GVb3lP*k2VSTDaEDXmcv{d5-`39k^kIB{SK5qo0O{EoG;=hIL`RLH)Af{JqFkPE&b5k{A( zOqT_|Ta936H{?R%z~M8%7x3@^lMhlfMR2)vsxi-oDcBnKjc5CKJ*tI7Lfq*8l`3oB zR+-ox2{NYk*=C$6_4gU6Bnp&(nR)0Efp6?3JX)lR;dpWK-Q#zCi`$8RRT(=;mIm9G z+-#g^|Hl`dHENKZSwLx9feDm-2No(->11`BlLgntV3t)d<=Rn*4u{(}%b#7sRRObr z%z*T8>$bL_PfbAFht#eT5df zHD*-H{g?>1cXm>rCZZgHh(i=l{^ikD2Ny^$?6hLnXcUZb1>G-9`aVI63(x-+=k9R! zZhmQF1eaX)n2%2EX(DYL6jJsk@uY!>E%Hkrz1ReWL0c9_5CMcY$XQE%d2Os+sdR&M zjqu)FSX!e5%MJ89v35i&S%iszEXajPd@ zcyD!zI4)I?YIILu5ZB;$Z;qNUy7pE%iVE5$b~Vn@ zeE3-DO?Lz(?H>>>t1B@XT(^70t~66Qk(4VuBb?Ejo&PQd_y-?KDovxL9q3*^FV}ko zNX;*Fpz$lf?#3(f<^le|GO1J3tj?)ooLepQP4Eea^=G8KF1jj>=uMN77$1zf s(G&9HZ)NybrWTgK|EKiz27S|ioAnuA@vUC`cZvffMP)^5g!BXd4~qten*aa+ diff --git a/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/hungry.png b/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/hungry.png index 45e00d6bf1fcd6553025a71055086e667d42c3d4..bc07df28ce7241cbb0fe68e511efd28ede2dbb85 100644 GIT binary patch delta 2885 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 literal 6739 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 S(Z2lJ0VO##*=lLC(EkC(Y~a}d diff --git a/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/plague.png b/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/plague.png index 629cbd64aa2680488204f8c7deb59d70561eea23..2ce6183d6209adfbbc737fb9a930e3b13d678711 100644 GIT binary patch literal 3720 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 literal 8346 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 G3i%(0|0xXs diff --git a/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/vampire_overlay.png b/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/vampire_overlay.png deleted file mode 100644 index d7910ebe3ee16b27494361f8d4d0caa506d8fc19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7768 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 From 30d5d16233502ff03cbf64762d3afd15a8289c76 Mon Sep 17 00:00:00 2001 From: FatherToast Date: Thu, 21 Jul 2022 17:31:15 -0500 Subject: [PATCH 11/16] Update themes a bit, add biome depth/scale --- .../common/bestiary/BestiaryInfo.java | 22 ++++++--------- .../config/field/EnvironmentListField.java | 15 ++++++++-- .../common/config/util/EnvironmentEntry.java | 28 +++++++++++++++++++ .../biome/TerrainDepthEnvironment.java | 27 ++++++++++++++++++ .../biome/TerrainScaleEnvironment.java | 27 ++++++++++++++++++ .../silverfish/FishingSilverfishEntity.java | 2 +- .../entity/zombie/FishingZombieEntity.java | 2 +- .../FishingZombifiedPiglinEntity.java | 2 +- 8 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TerrainDepthEnvironment.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TerrainScaleEnvironment.java diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java b/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java index 88e9e8a..d48da85 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java @@ -57,18 +57,18 @@ public class BestiaryInfo { DESERT( new EnvironmentList( EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inUltraWarmDimension().build(), EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inDryBiome().build(), + EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).inWaterBiome().build(), EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).inHumidBiome().build(), - EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).inBiomeCategory( BiomeCategory.OCEAN ).build(), - EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).inBiomeCategory( BiomeCategory.RIVER ).build(), - EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).isRaining().canSeeSky().build() + EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).isRaining().canSeeSky().build(), + EnvironmentEntry.builder( DefaultWeight.HIGH.value ).belowHalfMoonLight().build() ) ), WATER( new EnvironmentList( EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).inUltraWarmDimension().build(), EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).inDryBiome().build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inWaterBiome().build(), EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inHumidBiome().build(), - EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.OCEAN ).build(), - EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.RIVER ).build(), - EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).isRaining().canSeeSky().build() + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).isRaining().canSeeSky().build(), + EnvironmentEntry.builder( DefaultWeight.HIGH.value ).aboveHalfMoonLight().build() ) ), FOREST( new EnvironmentList( EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.TAIGA ).build(), @@ -78,17 +78,13 @@ public class BestiaryInfo { EnvironmentEntry.builder( DefaultWeight.HIGH.value ).atMaxMoonLight().build() ) ), MOUNTAIN( new EnvironmentList( - EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.EXTREME_HILLS ).build(), - EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.MESA ).build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inMountainBiome().build(), EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).aboveMountainLevel().build(), EnvironmentEntry.builder( DefaultWeight.HIGH.value ).atNoMoonLight().build(), - EnvironmentEntry.builder( DefaultWeight.LOW.value ).belowSeaFloor().build() + EnvironmentEntry.builder( DefaultWeight.LOW.value ).belowSeaLevel().build() ) ), FISHING( new EnvironmentList( - EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.OCEAN ).build(), - EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.RIVER ).build(), - EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.BEACH ).build(), - EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inBiomeCategory( BiomeCategory.BEACH ).build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inWaterBiome().build(), EnvironmentEntry.builder( DefaultWeight.HIGH.value ).atMaxMoonLight().build(), EnvironmentEntry.builder( DefaultWeight.HIGH.value ).isRaining().notInDryBiome().build() ) ); diff --git a/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java b/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java index 21abdb5..b9cc7e7 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java +++ b/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java @@ -27,7 +27,7 @@ public class EnvironmentListField extends GenericField { /* When adding new environment conditions, you must: * - Add a new constant for its name here * - Create the environment class and ensure #name() returns the new name constant - * - Link the name to construction in #parseCondition(String,String) below + * - Link the name to construction and valid name array in #parseCondition(String,String) below * - Add any applicable builder methods in EnvironmentEntry.Builder * - Describe the new environment condition's usage/format in #environmentDescriptions() below */ @@ -35,6 +35,8 @@ public class EnvironmentListField extends GenericField { public static final String ENV_DIMENSION_PROPERTY = "dimension_property"; public static final String ENV_DIMENSION_TYPE = "dimension_type"; // Biome-based + public static final String ENV_TERRAIN_DEPTH = "terrain_depth"; + public static final String ENV_TERRAIN_SCALE = "terrain_scale"; public static final String ENV_RAINFALL = "rainfall"; public static final String ENV_BIOME_TEMPERATURE = "biome_temp"; public static final String ENV_TEMPERATURE = "temp"; @@ -84,6 +86,11 @@ public class EnvironmentListField extends GenericField { comment.add( " \"" + ENV_DIMENSION_TYPE + " (!)namespace:dimension_type_name\":" ); comment.add( " The world's dimension type. In vanilla, these are only \"minecraft:overworld\", \"minecraft:the_nether\", or \"minecraft:the_end\"." ); // Biome-based + comment.add( " \"" + ENV_TERRAIN_DEPTH + " op value\":" );//TODO see if this changes in MC 1.18 + comment.add( " Biome's depth parameter. A measure of how high the terrain generates; depth < 0 makes a watery biome. For reference, generally vanilla" ); + comment.add( " plateaus are 1.5, mountains are 1, plains are 0.125, swamps are -0.2, rivers are -0.5, oceans are -1, and deep oceans are -1.8." ); + comment.add( " \"" + ENV_TERRAIN_SCALE + " op value\":" ); + comment.add( " Biome's scale parameter. A measure of how 'wavy' the terrain generates. For reference, generally vanilla mountains are 0.5 and plains are 0.05." ); comment.add( " \"" + ENV_RAINFALL + " op value\":" ); comment.add( " Biome's rainfall parameter. If this is \"= 0\", it checks that rain is disabled. For reference, rainfall > 0.85 suppresses fire." ); comment.add( " \"" + ENV_BIOME_TEMPERATURE + " op value\" or \"" + ENV_BIOME_TEMPERATURE + " (!)" + TemperatureEnvironment.FREEZING + "\":" ); @@ -228,6 +235,10 @@ public class EnvironmentListField extends GenericField { case ENV_DIMENSION_TYPE: return value.endsWith( "*" ) ? new DimensionTypeGroupEnvironment( this, value ) : new DimensionTypeEnvironment( this, value ); // Biome-based + case ENV_TERRAIN_DEPTH: + return new TerrainDepthEnvironment( this, value ); + case ENV_TERRAIN_SCALE: + return new TerrainScaleEnvironment( this, value ); case ENV_RAINFALL: return new RainfallEnvironment( this, value ); case ENV_BIOME_TEMPERATURE: @@ -273,7 +284,7 @@ public class EnvironmentListField extends GenericField { // Dimension-based ENV_DIMENSION_PROPERTY, ENV_DIMENSION_TYPE, // Biome-based - ENV_RAINFALL, ENV_BIOME_TEMPERATURE, ENV_TEMPERATURE, ENV_BIOME_CATEGORY, ENV_BIOME, + ENV_TERRAIN_DEPTH, ENV_TERRAIN_SCALE, ENV_RAINFALL, ENV_BIOME_TEMPERATURE, ENV_TEMPERATURE, ENV_BIOME_CATEGORY, ENV_BIOME, // Position-based ENV_STRUCTURE, ENV_Y, ENV_Y_FROM_SEA, ENV_POSITION, // Time-based diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java index a0bc5df..b73fa1d 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java @@ -138,6 +138,34 @@ public class EnvironmentEntry { // Biome-based + /** Check if the biome is a water or beach biome (depth at or below sea level). */ + public Builder inWaterBiome() { return isDepth( ComparisonOperator.LESS_OR_EQUAL, 0.0F ); } + + /** Check if the biome is a water or beach biome (depth at or below sea level). */ + public Builder notInWaterBiome() { return isDepth( ComparisonOperator.LESS_OR_EQUAL.invert(), 0.0F ); } + + /** Check if the biome has relatively high elevation. */ + public Builder inMountainBiome() { return isDepth( ComparisonOperator.GREATER_OR_EQUAL, 0.4F ); } + + /** Check if the biome has relatively high elevation. */ + public Builder notInMountainBiome() { return isDepth( ComparisonOperator.GREATER_OR_EQUAL.invert(), 0.4F ); } + + private Builder isDepth( ComparisonOperator op, float value ) { return in( new TerrainDepthEnvironment( op, value ) ); } + + /** Check if the biome is relatively flat. */ + public Builder inFlatBiome() { return isScale( ComparisonOperator.LESS_OR_EQUAL, 0.1F ); } + + /** Check if the biome is relatively flat. */ + public Builder notInFlatBiome() { return isScale( ComparisonOperator.LESS_OR_EQUAL.invert(), 0.1F ); } + + /** Check if the biome is relatively hilly. */ + public Builder inHillyBiome() { return isScale( ComparisonOperator.GREATER_OR_EQUAL, 0.3F ); } + + /** Check if the biome is relatively hilly. */ + public Builder notInHillyBiome() { return isScale( ComparisonOperator.GREATER_OR_EQUAL.invert(), 0.3F ); } + + private Builder isScale( ComparisonOperator op, float value ) { return in( new TerrainScaleEnvironment( op, value ) ); } + /** Check if the biome has rain disabled. */ public Builder inDryBiome() { return inAvgRainfall( ComparisonOperator.EQUAL_TO, 0.0F ); } diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TerrainDepthEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TerrainDepthEnvironment.java new file mode 100644 index 0000000..f58b719 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TerrainDepthEnvironment.java @@ -0,0 +1,27 @@ +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 TerrainDepthEnvironment extends CompareFloatEnvironment { + + public TerrainDepthEnvironment( ComparisonOperator op, float value ) { super( op, value ); } + + public TerrainDepthEnvironment( 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_TERRAIN_DEPTH; } + + /** @return Returns the actual value to compare, or Float.NaN if there isn't enough information. */ + @Override + public float getActual( World world, @Nullable BlockPos pos ) { + return pos == null ? Float.NaN : world.getBiome( pos ).getDepth(); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TerrainScaleEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TerrainScaleEnvironment.java new file mode 100644 index 0000000..1b0ac3e --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TerrainScaleEnvironment.java @@ -0,0 +1,27 @@ +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 TerrainScaleEnvironment extends CompareFloatEnvironment { + + public TerrainScaleEnvironment( ComparisonOperator op, float value ) { super( op, value ); } + + public TerrainScaleEnvironment( 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_TERRAIN_SCALE; } + + /** @return Returns the actual value to compare, or Float.NaN if there isn't enough information. */ + @Override + public float getActual( World world, @Nullable BlockPos pos ) { + return pos == null ? Float.NaN : world.getBiome( pos ).getScale(); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/FishingSilverfishEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/FishingSilverfishEntity.java index df88d0c..09ab124 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/FishingSilverfishEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/FishingSilverfishEntity.java @@ -25,7 +25,7 @@ public class FishingSilverfishEntity extends _SpecialSilverfishEntity implements @SpecialMob.BestiaryInfoSupplier public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { - bestiaryInfo.color( 0x2D41F4 ).theme( BestiaryInfo.Theme.FISHING ) + bestiaryInfo.color( 0x2D41F4 ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.FISHING ) .uniqueTextureBaseOnly() .size( 1.2F, 0.5F, 0.4F ) .addExperience( 2 ).drownImmune().fluidPushImmune() diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombie/FishingZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/FishingZombieEntity.java index 42742e4..523e0d0 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/zombie/FishingZombieEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombie/FishingZombieEntity.java @@ -40,7 +40,7 @@ public class FishingZombieEntity extends _SpecialZombieEntity implements IAngler @SpecialMob.BestiaryInfoSupplier public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { - bestiaryInfo.color( 0x2D41F4 ).theme( BestiaryInfo.Theme.FISHING ) + bestiaryInfo.color( 0x2D41F4 ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.FISHING ) .addExperience( 2 ).drownImmune().fluidPushImmune() .bowAttack( 0.0, 1.0, 1.0, 40, 10.0 ) .multiplyAttribute( Attributes.MOVEMENT_SPEED, 0.8 ); diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/FishingZombifiedPiglinEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/FishingZombifiedPiglinEntity.java index 9befadf..00150e6 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/FishingZombifiedPiglinEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/FishingZombifiedPiglinEntity.java @@ -40,7 +40,7 @@ public class FishingZombifiedPiglinEntity extends _SpecialZombifiedPiglinEntity @SpecialMob.BestiaryInfoSupplier public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { - bestiaryInfo.color( 0x2D41F4 ).theme( BestiaryInfo.Theme.FISHING ) + bestiaryInfo.color( 0x2D41F4 ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.FISHING ) .addExperience( 2 ).drownImmune().fluidPushImmune() .bowAttack( 0.0, 1.0, 1.0, 40, 10.0 ) .multiplyAttribute( Attributes.MOVEMENT_SPEED, 0.8 ); From 5f8a870b375533a4e242be19c29c3c3e4fb31e4d Mon Sep 17 00:00:00 2001 From: FatherToast Date: Thu, 21 Jul 2022 19:07:58 -0500 Subject: [PATCH 12/16] General cleanup and fixes --- .../client/ClientEventHandler.java | 5 - .../specialmobs/client/ClientRegister.java | 5 +- .../specialmobs/client/misc/package-info.java | 7 + .../specialmobs/client/package-info.java | 7 + .../entity/CorporealShiftGhastModel.java | 45 ++-- .../entity/CorporealShiftGhastRenderer.java | 6 +- .../entity/NinjaSkeletonRenderer.java | 11 +- .../renderer/entity/SpecialBlazeRenderer.java | 5 - .../entity/SpecialCreeperRenderer.java | 5 - .../entity/SpecialEndermanRenderer.java | 5 - .../renderer/entity/SpecialGhastRenderer.java | 7 +- .../entity/SpecialMagmaCubeRenderer.java | 5 - .../entity/SpecialPiglinRenderer.java | 5 - .../entity/SpecialSilverfishRenderer.java | 5 - .../entity/SpecialSkeletonRenderer.java | 5 - .../renderer/entity/SpecialSlimeRenderer.java | 5 - .../entity/SpecialSpiderRenderer.java | 5 - .../renderer/entity/SpecialWitchRenderer.java | 5 - .../entity/SpecialZombieRenderer.java | 25 +- .../entity/SpecialZombieVillagerRenderer.java | 5 - .../layers/SpecialCreeperChargeLayer.java | 43 ++-- .../entity/layers/SpecialGhastEyesLayer.java | 23 +- .../entity/layers/SpecialMobEyesLayer.java | 18 +- .../entity/layers/SpecialMobOverlayLayer.java | 5 - .../renderer/entity/layers/package-info.java | 7 + .../client/renderer/entity/package-info.java | 7 + .../common/bestiary/BestiaryInfo.java | 5 + .../biome/RainfallEnvironment.java | 2 + .../specialmobs/common/core/SpecialMobs.java | 9 +- .../common/entity/SpecialMobData.java | 238 +++++++++--------- .../common/entity/ai/AIHelper.java | 1 + .../entity/witch/_SpecialWitchEntity.java | 27 +- .../zombie/MadScientistZombieEntity.java | 2 +- .../datagen/SMLanguageProvider.java | 19 +- 34 files changed, 275 insertions(+), 304 deletions(-) create mode 100644 src/main/java/fathertoast/specialmobs/client/misc/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/client/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/client/renderer/entity/package-info.java diff --git a/src/main/java/fathertoast/specialmobs/client/ClientEventHandler.java b/src/main/java/fathertoast/specialmobs/client/ClientEventHandler.java index d63e104..d7eb327 100644 --- a/src/main/java/fathertoast/specialmobs/client/ClientEventHandler.java +++ b/src/main/java/fathertoast/specialmobs/client/ClientEventHandler.java @@ -2,7 +2,6 @@ package fathertoast.specialmobs.client; import fathertoast.specialmobs.common.config.Config; import fathertoast.specialmobs.common.core.SpecialMobs; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screen.Screen; import net.minecraft.util.Util; @@ -14,10 +13,6 @@ import net.minecraftforge.fml.ExtensionPoint; import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @Mod.EventBusSubscriber( value = Dist.CLIENT, modid = SpecialMobs.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE ) public class ClientEventHandler { diff --git a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java index 3712786..8984995 100644 --- a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java +++ b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java @@ -9,7 +9,6 @@ import fathertoast.specialmobs.common.entity.skeleton.NinjaSkeletonEntity; import fathertoast.specialmobs.common.entity.witherskeleton.NinjaWitherSkeletonEntity; import fathertoast.specialmobs.common.entity.zombie.MadScientistZombieEntity; import fathertoast.specialmobs.common.entity.zombifiedpiglin.VampireZombifiedPiglinEntity; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.ItemRenderer; import net.minecraft.client.renderer.entity.SpriteRenderer; @@ -24,11 +23,8 @@ import net.minecraftforge.fml.client.registry.RenderingRegistry; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; -import javax.annotation.ParametersAreNonnullByDefault; import java.util.function.Supplier; -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @Mod.EventBusSubscriber( value = Dist.CLIENT, modid = SpecialMobs.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD ) public class ClientRegister { @@ -78,6 +74,7 @@ public class ClientRegister { RenderingRegistry.registerEntityRenderingHandler( species.entityType.get(), renderFactory ); } + @SuppressWarnings( "SameParameterValue" ) private static void registerSpriteRenderer( EntityType entityType, Supplier minecraftSupplier, float scale, boolean fullBright ) { ItemRenderer itemRenderer = minecraftSupplier.get().getItemRenderer(); RenderingRegistry.registerEntityRenderingHandler( entityType, ( renderManager ) -> new SpriteRenderer<>( renderManager, itemRenderer, scale, fullBright ) ); diff --git a/src/main/java/fathertoast/specialmobs/client/misc/package-info.java b/src/main/java/fathertoast/specialmobs/client/misc/package-info.java new file mode 100644 index 0000000..c8114bd --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/client/misc/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.client.misc; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/client/package-info.java b/src/main/java/fathertoast/specialmobs/client/package-info.java new file mode 100644 index 0000000..49b32bc --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/client/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.client; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/CorporealShiftGhastModel.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/CorporealShiftGhastModel.java index c6be818..fd15ff3 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/CorporealShiftGhastModel.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/CorporealShiftGhastModel.java @@ -7,49 +7,54 @@ import net.minecraft.client.renderer.model.ModelRenderer; import net.minecraft.entity.Entity; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.MathHelper; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import java.util.Random; import java.util.function.Function; /** Copy of {@link net.minecraft.client.renderer.entity.model.GhastModel} */ +@OnlyIn( Dist.CLIENT ) public class CorporealShiftGhastModel extends SegmentedModel { private final ModelRenderer[] tentacles = new ModelRenderer[9]; private final ImmutableList parts; - + public CorporealShiftGhastModel() { ImmutableList.Builder builder = ImmutableList.builder(); - ModelRenderer modelrenderer = new ModelRenderer(this, 0, 0); - modelrenderer.addBox(-8.0F, -8.0F, -8.0F, 16.0F, 16.0F, 16.0F); + ModelRenderer modelrenderer = new ModelRenderer( this, 0, 0 ); + modelrenderer.addBox( -8.0F, -8.0F, -8.0F, 16.0F, 16.0F, 16.0F ); modelrenderer.y = 17.6F; - builder.add(modelrenderer); - Random random = new Random(1660L); - - for (int i = 0; i < this.tentacles.length; ++i) { - this.tentacles[i] = new ModelRenderer(this, 0, 0); + builder.add( modelrenderer ); + Random random = new Random( 1660L ); + + for( int i = 0; i < this.tentacles.length; ++i ) { + this.tentacles[i] = new ModelRenderer( this, 0, 0 ); float f = (((float) (i % 3) - (float) (i / 3 % 2) * 0.5F + 0.25F) / 2.0F * 2.0F - 1.0F) * 5.0F; float f1 = ((float) (i / 3) / 2.0F * 2.0F - 1.0F) * 5.0F; - int j = random.nextInt(7) + 8; - this.tentacles[i].addBox(-1.0F, 0.0F, -1.0F, 2.0F, (float) j, 2.0F); + int j = random.nextInt( 7 ) + 8; + this.tentacles[i].addBox( -1.0F, 0.0F, -1.0F, 2.0F, (float) j, 2.0F ); this.tentacles[i].x = f; this.tentacles[i].z = f1; this.tentacles[i].y = 24.6F; - builder.add(this.tentacles[i]); + builder.add( this.tentacles[i] ); } - + this.parts = builder.build(); } - - public void setRenderType(Function renderTypeFunc) { + + public void setRenderType( Function renderTypeFunc ) { this.renderType = renderTypeFunc; } - - public void setupAnim(T ghast, float p_225597_2_, float p_225597_3_, float p_225597_4_, float p_225597_5_, float p_225597_6_) { - for (int i = 0; i < this.tentacles.length; ++i) { - this.tentacles[i].xRot = 0.2F * MathHelper.sin(p_225597_4_ * 0.3F + (float) i) + 0.4F; + + @Override + public void setupAnim( T ghast, float p_225597_2_, float p_225597_3_, float p_225597_4_, float p_225597_5_, float p_225597_6_ ) { + for( int i = 0; i < this.tentacles.length; ++i ) { + this.tentacles[i].xRot = 0.2F * MathHelper.sin( p_225597_4_ * 0.3F + (float) i ) + 0.4F; } } - + + @Override public Iterable parts() { return this.parts; } -} +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/CorporealShiftGhastRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/CorporealShiftGhastRenderer.java index bb94edf..b8c17f7 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/CorporealShiftGhastRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/CorporealShiftGhastRenderer.java @@ -7,7 +7,6 @@ import fathertoast.specialmobs.common.core.SpecialMobs; import fathertoast.specialmobs.common.entity.ISpecialMob; import fathertoast.specialmobs.common.entity.SpecialMobData; import fathertoast.specialmobs.common.entity.ghast.CorporealShiftGhastEntity; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.entity.EntityRendererManager; @@ -16,11 +15,8 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; import java.util.function.Function; -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class CorporealShiftGhastRenderer extends MobRenderer> { @@ -47,7 +43,7 @@ public class CorporealShiftGhastRenderer extends MobRenderer data = ((ISpecialMob) entity).getSpecialData(); - return entity.isCharging() && data.hasOverlayTexture() ? data.getTextureOverlay() : data.getTexture(); + return entity.isCharging() && data.getTextureOverlay() != null ? data.getTextureOverlay() : data.getTexture(); } @Override diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/NinjaSkeletonRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/NinjaSkeletonRenderer.java index 412e5af..af84142 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/NinjaSkeletonRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/NinjaSkeletonRenderer.java @@ -5,7 +5,6 @@ import com.mojang.blaze3d.vertex.IVertexBuilder; import com.mojang.blaze3d.vertex.MatrixApplyingVertexBuilder; import com.mojang.blaze3d.vertex.VertexBuilderUtils; import fathertoast.specialmobs.common.entity.ai.INinja; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.block.BlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.*; @@ -30,11 +29,9 @@ import javax.annotation.ParametersAreNonnullByDefault; import java.util.Random; import java.util.SortedSet; -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class NinjaSkeletonRenderer extends SpecialSkeletonRenderer { - + private final BlockRendererDispatcher blockRenderer; public NinjaSkeletonRenderer( EntityRendererManager rendererManager ) { @@ -45,10 +42,10 @@ public class NinjaSkeletonRenderer extends SpecialSkeletonRenderer { @Override public void render( AbstractSkeletonEntity entity, float rotation, float partialTicks, MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight ) { - + INinja ninja = (INinja) entity; final BlockState disguiseBlock = ninja.getHiddenDragon(); - + if( disguiseBlock == null ) { super.render( entity, rotation, partialTicks, matrixStack, buffer, packedLight ); } @@ -61,7 +58,7 @@ public class NinjaSkeletonRenderer extends SpecialSkeletonRenderer { private void renderBlockDisguise( BlockState block, BlockPos pos, IBlockDisplayReader displayReader, MatrixStack matrixStack, IRenderTypeBuffer buffer, Random random ) { matrixStack.pushPose(); matrixStack.translate( -0.5, 0.0, -0.5 ); - blockRenderer.renderModel(block, pos, displayReader, matrixStack, buffer.getBuffer(RenderType.cutout()), false, random, EmptyModelData.INSTANCE); + blockRenderer.renderModel( block, pos, displayReader, matrixStack, buffer.getBuffer( RenderType.cutout() ), false, random, EmptyModelData.INSTANCE ); matrixStack.popPose(); } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialBlazeRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialBlazeRenderer.java index 474cde4..2296ca6 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialBlazeRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialBlazeRenderer.java @@ -2,7 +2,6 @@ package fathertoast.specialmobs.client.renderer.entity; import com.mojang.blaze3d.matrix.MatrixStack; import fathertoast.specialmobs.common.entity.ISpecialMob; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.BlazeRenderer; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.entity.monster.BlazeEntity; @@ -10,10 +9,6 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialBlazeRenderer extends BlazeRenderer { diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialCreeperRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialCreeperRenderer.java index ba1cade..c27b038 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialCreeperRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialCreeperRenderer.java @@ -5,7 +5,6 @@ import fathertoast.specialmobs.client.renderer.entity.layers.SpecialCreeperCharg import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobEyesLayer; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobOverlayLayer; import fathertoast.specialmobs.common.entity.ISpecialMob; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.CreeperRenderer; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.entity.layers.CreeperChargeLayer; @@ -15,10 +14,6 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialCreeperRenderer extends CreeperRenderer { diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialEndermanRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialEndermanRenderer.java index b61e9c7..90e0e2b 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialEndermanRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialEndermanRenderer.java @@ -4,7 +4,6 @@ import com.mojang.blaze3d.matrix.MatrixStack; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobEyesLayer; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobOverlayLayer; import fathertoast.specialmobs.common.entity.ISpecialMob; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.EndermanRenderer; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.entity.layers.EndermanEyesLayer; @@ -14,10 +13,6 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialEndermanRenderer extends EndermanRenderer { diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialGhastRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialGhastRenderer.java index ea724e9..74e01bd 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialGhastRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialGhastRenderer.java @@ -3,7 +3,6 @@ package fathertoast.specialmobs.client.renderer.entity; import com.mojang.blaze3d.matrix.MatrixStack; import fathertoast.specialmobs.common.entity.ISpecialMob; import fathertoast.specialmobs.common.entity.SpecialMobData; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.entity.GhastRenderer; import net.minecraft.entity.monster.GhastEntity; @@ -11,10 +10,6 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialGhastRenderer extends GhastRenderer { @@ -32,7 +27,7 @@ public class SpecialGhastRenderer extends GhastRenderer { @Override public ResourceLocation getTextureLocation( GhastEntity entity ) { final SpecialMobData data = ((ISpecialMob) entity).getSpecialData(); - return entity.isCharging() && data.hasOverlayTexture() ? data.getTextureOverlay() : data.getTexture(); + return entity.isCharging() && data.getTextureOverlay() != null ? data.getTextureOverlay() : data.getTexture(); } @Override diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialMagmaCubeRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialMagmaCubeRenderer.java index fad4675..7f20ff2 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialMagmaCubeRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialMagmaCubeRenderer.java @@ -2,7 +2,6 @@ package fathertoast.specialmobs.client.renderer.entity; import com.mojang.blaze3d.matrix.MatrixStack; import fathertoast.specialmobs.common.entity.ISpecialMob; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.entity.MagmaCubeRenderer; import net.minecraft.entity.monster.MagmaCubeEntity; @@ -10,10 +9,6 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialMagmaCubeRenderer extends MagmaCubeRenderer { diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialPiglinRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialPiglinRenderer.java index 4408e0f..e1cd338 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialPiglinRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialPiglinRenderer.java @@ -4,7 +4,6 @@ import com.mojang.blaze3d.matrix.MatrixStack; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobEyesLayer; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobOverlayLayer; import fathertoast.specialmobs.common.entity.ISpecialMob; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.entity.PiglinRenderer; import net.minecraft.client.renderer.entity.model.PiglinModel; @@ -13,10 +12,6 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialPiglinRenderer extends PiglinRenderer { diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSilverfishRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSilverfishRenderer.java index 0b7ea34..0572aa7 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSilverfishRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSilverfishRenderer.java @@ -3,7 +3,6 @@ package fathertoast.specialmobs.client.renderer.entity; import com.mojang.blaze3d.matrix.MatrixStack; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobEyesLayer; import fathertoast.specialmobs.common.entity.ISpecialMob; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.entity.SilverfishRenderer; import net.minecraft.entity.monster.SilverfishEntity; @@ -11,10 +10,6 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialSilverfishRenderer extends SilverfishRenderer { diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSkeletonRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSkeletonRenderer.java index 61f12df..a92788c 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSkeletonRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSkeletonRenderer.java @@ -4,7 +4,6 @@ import com.mojang.blaze3d.matrix.MatrixStack; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobEyesLayer; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobOverlayLayer; import fathertoast.specialmobs.common.entity.ISpecialMob; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.entity.SkeletonRenderer; import net.minecraft.client.renderer.entity.model.SkeletonModel; @@ -13,10 +12,6 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialSkeletonRenderer extends SkeletonRenderer { diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSlimeRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSlimeRenderer.java index 4b4f521..d4678ea 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSlimeRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSlimeRenderer.java @@ -3,7 +3,6 @@ package fathertoast.specialmobs.client.renderer.entity; import com.mojang.blaze3d.matrix.MatrixStack; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobEyesLayer; import fathertoast.specialmobs.common.entity.ISpecialMob; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.entity.SlimeRenderer; import net.minecraft.entity.monster.SlimeEntity; @@ -11,10 +10,6 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialSlimeRenderer extends SlimeRenderer { diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSpiderRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSpiderRenderer.java index 2a7764e..735c716 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSpiderRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSpiderRenderer.java @@ -3,7 +3,6 @@ package fathertoast.specialmobs.client.renderer.entity; import com.mojang.blaze3d.matrix.MatrixStack; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobEyesLayer; import fathertoast.specialmobs.common.entity.ISpecialMob; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.entity.SpiderRenderer; import net.minecraft.client.renderer.entity.layers.SpiderEyesLayer; @@ -12,10 +11,6 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialSpiderRenderer extends SpiderRenderer { diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialWitchRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialWitchRenderer.java index 87b2cd0..3cc2256 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialWitchRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialWitchRenderer.java @@ -4,7 +4,6 @@ import com.mojang.blaze3d.matrix.MatrixStack; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobEyesLayer; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobOverlayLayer; import fathertoast.specialmobs.common.entity.ISpecialMob; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.entity.WitchRenderer; import net.minecraft.client.renderer.entity.model.WitchModel; @@ -13,10 +12,6 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialWitchRenderer extends WitchRenderer { diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialZombieRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialZombieRenderer.java index 63ce9c0..4864b9f 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialZombieRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialZombieRenderer.java @@ -4,7 +4,6 @@ import com.mojang.blaze3d.matrix.MatrixStack; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobEyesLayer; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobOverlayLayer; import fathertoast.specialmobs.common.entity.ISpecialMob; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.entity.ZombieRenderer; import net.minecraft.client.renderer.entity.model.ZombieModel; @@ -13,33 +12,29 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialZombieRenderer extends ZombieRenderer { - + private final float baseShadowRadius; - - public SpecialZombieRenderer(EntityRendererManager rendererManager) { - super(rendererManager); + + public SpecialZombieRenderer( EntityRendererManager rendererManager ) { + super( rendererManager ); baseShadowRadius = shadowRadius; addLayer( new SpecialMobEyesLayer<>( this ) ); addLayer( new SpecialMobOverlayLayer<>( this, new ZombieModel<>( 0.25F, true ) ) ); } - + @Override - public ResourceLocation getTextureLocation(ZombieEntity entity ) { + public ResourceLocation getTextureLocation( ZombieEntity entity ) { return ((ISpecialMob) entity).getSpecialData().getTexture(); } - + @Override - protected void scale(ZombieEntity entity, MatrixStack matrixStack, float partialTick ) { + protected void scale( ZombieEntity entity, MatrixStack matrixStack, float partialTick ) { super.scale( entity, matrixStack, partialTick ); - + final float scale = ((ISpecialMob) entity).getSpecialData().getRenderScale(); shadowRadius = baseShadowRadius * scale; matrixStack.scale( scale, scale, scale ); } -} +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialZombieVillagerRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialZombieVillagerRenderer.java index 1c3d7a6..0161e29 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialZombieVillagerRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialZombieVillagerRenderer.java @@ -4,7 +4,6 @@ import com.mojang.blaze3d.matrix.MatrixStack; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobEyesLayer; import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobOverlayLayer; import fathertoast.specialmobs.common.entity.ISpecialMob; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.BipedRenderer; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.entity.layers.BipedArmorLayer; @@ -14,10 +13,6 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialZombieVillagerRenderer extends BipedRenderer> { diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialCreeperChargeLayer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialCreeperChargeLayer.java index 2cb715c..edc8a80 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialCreeperChargeLayer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialCreeperChargeLayer.java @@ -12,38 +12,41 @@ import net.minecraft.client.renderer.entity.model.EntityModel; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.entity.monster.CreeperEntity; import net.minecraft.util.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +@OnlyIn( Dist.CLIENT ) public class SpecialCreeperChargeLayer> extends LayerRenderer { - + private static final ResourceLocation[] CHARGED = new ResourceLocation[] { - new ResourceLocation("textures/entity/creeper/creeper_armor.png"), - SpecialMobs.resourceLoc("textures/entity/creeper/super_charged.png") + new ResourceLocation( "textures/entity/creeper/creeper_armor.png" ), + SpecialMobs.resourceLoc( "textures/entity/creeper/super_charged.png" ) }; - + private final M model; - - + + public SpecialCreeperChargeLayer( IEntityRenderer renderer, M model ) { super( renderer ); this.model = model; } - + @Override - public void render(MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight, T creeper, float limbSwing, float limbSwingAmount, float partialTicks, float ageInTicks, float netHeadYaw, float headPitch) { - if (creeper.isPowered() || ((_SpecialCreeperEntity)creeper).isSupercharged()) { - float tickAndPartial = (float)creeper.tickCount + partialTicks; + public void render( MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight, T creeper, float limbSwing, float limbSwingAmount, float partialTicks, float ageInTicks, float netHeadYaw, float headPitch ) { + if( creeper.isPowered() || ((_SpecialCreeperEntity) creeper).isSupercharged() ) { + float tickAndPartial = (float) creeper.tickCount + partialTicks; float textureOffset = tickAndPartial * 0.01F; - - model.prepareMobModel(creeper, limbSwing, limbSwingAmount, partialTicks); - this.getParentModel().copyPropertiesTo(model); - IVertexBuilder ivertexbuilder = buffer.getBuffer(RenderType.energySwirl(this.getTextureLocation(creeper), textureOffset, textureOffset)); - model.setupAnim(creeper, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch); - model.renderToBuffer(matrixStack, ivertexbuilder, packedLight, OverlayTexture.NO_OVERLAY, 0.5F, 0.5F, 0.5F, 1.0F); + + model.prepareMobModel( creeper, limbSwing, limbSwingAmount, partialTicks ); + this.getParentModel().copyPropertiesTo( model ); + IVertexBuilder ivertexbuilder = buffer.getBuffer( RenderType.energySwirl( this.getTextureLocation( creeper ), textureOffset, textureOffset ) ); + model.setupAnim( creeper, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch ); + model.renderToBuffer( matrixStack, ivertexbuilder, packedLight, OverlayTexture.NO_OVERLAY, 0.5F, 0.5F, 0.5F, 1.0F ); } } - + @Override - protected ResourceLocation getTextureLocation(CreeperEntity creeper) { - return ((_SpecialCreeperEntity)creeper).isSupercharged() ? CHARGED[1] : CHARGED[0]; + protected ResourceLocation getTextureLocation( CreeperEntity creeper ) { + return ((_SpecialCreeperEntity) creeper).isSupercharged() ? CHARGED[1] : CHARGED[0]; } -} +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialGhastEyesLayer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialGhastEyesLayer.java index eea53c5..6318dbb 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialGhastEyesLayer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialGhastEyesLayer.java @@ -12,31 +12,34 @@ import net.minecraft.client.renderer.entity.model.EntityModel; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.entity.monster.GhastEntity; import net.minecraft.util.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +@OnlyIn( Dist.CLIENT ) public class SpecialGhastEyesLayer> extends AbstractEyesLayer { private final RenderType FALLBACK = RenderType.eyes( new ResourceLocation( "textures/entity/spider_eyes.png" ) ); private final ResourceLocation eyes; private final ResourceLocation shootEyes; - - + + public SpecialGhastEyesLayer( IEntityRenderer renderer, ResourceLocation eyes, ResourceLocation shootEyes ) { super( renderer ); this.eyes = eyes; this.shootEyes = shootEyes; } - + @Override - public void render(MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight, T ghast, float limbSwing, - float limbSwingAmount, float partialTicks, float ageInTicks, float netHeadYaw, float headPitch ) { - - IVertexBuilder vertexBuilder = buffer.getBuffer( RenderType.entityCutout(ghast.isCharging() ? shootEyes : eyes) ); - this.getParentModel().renderToBuffer( matrixStack, vertexBuilder, LightTexture.pack(15, 15), OverlayTexture.NO_OVERLAY, + public void render( MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight, T ghast, float limbSwing, + float limbSwingAmount, float partialTicks, float ageInTicks, float netHeadYaw, float headPitch ) { + + final IVertexBuilder vertexBuilder = buffer.getBuffer( RenderType.entityCutout( ghast.isCharging() ? shootEyes : eyes ) ); + getParentModel().renderToBuffer( matrixStack, vertexBuilder, LightTexture.pack( 15, 15 ), OverlayTexture.NO_OVERLAY, 1.0F, 1.0F, 1.0F, 1.0F ); } - + @Override public RenderType renderType() { SpecialMobs.LOG.warn( "Something is attempting to get eye layer 'render type' for some reason! :(" ); return FALLBACK; } -} +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialMobEyesLayer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialMobEyesLayer.java index df3e70b..bdeb9f2 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialMobEyesLayer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialMobEyesLayer.java @@ -4,7 +4,6 @@ import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.vertex.IVertexBuilder; import fathertoast.specialmobs.common.core.SpecialMobs; import fathertoast.specialmobs.common.entity.ISpecialMob; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.RenderType; @@ -17,27 +16,20 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialMobEyesLayer> extends AbstractEyesLayer { - private final RenderType FALLBACK = RenderType.eyes( new ResourceLocation( "textures/entity/spider_eyes.png" ) ); + private static final RenderType FALLBACK = RenderType.eyes( new ResourceLocation( "textures/entity/spider_eyes.png" ) ); + + public SpecialMobEyesLayer( IEntityRenderer renderer ) { super( renderer ); } - public SpecialMobEyesLayer( IEntityRenderer renderer ) { - super( renderer ); - } - @Override public void render( MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight, T entity, float limbSwing, float limbSwingAmount, float partialTicks, float ageInTicks, float netHeadYaw, float headPitch ) { final ResourceLocation eyesTexture = ((ISpecialMob) entity).getSpecialData().getTextureEyes(); if( eyesTexture == null ) return; - //TODO does not work; for some reason, all the transparency renders as white - IVertexBuilder vertexBuilder = buffer.getBuffer( RenderType.eyes(eyesTexture) ); - this.getParentModel().renderToBuffer( matrixStack, vertexBuilder, LightTexture.pack(15, 15), OverlayTexture.NO_OVERLAY, + final IVertexBuilder vertexBuilder = buffer.getBuffer( RenderType.eyes( eyesTexture ) ); + getParentModel().renderToBuffer( matrixStack, vertexBuilder, LightTexture.pack( 15, 15 ), OverlayTexture.NO_OVERLAY, 1.0F, 1.0F, 1.0F, 1.0F ); } diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialMobOverlayLayer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialMobOverlayLayer.java index 77f1980..a7d6021 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialMobOverlayLayer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/SpecialMobOverlayLayer.java @@ -3,7 +3,6 @@ package fathertoast.specialmobs.client.renderer.entity.layers; import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.vertex.IVertexBuilder; import fathertoast.specialmobs.common.entity.ISpecialMob; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.entity.IEntityRenderer; @@ -15,10 +14,6 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault @OnlyIn( Dist.CLIENT ) public class SpecialMobOverlayLayer> extends LayerRenderer { diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/package-info.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/package-info.java new file mode 100644 index 0000000..5dcd3a2 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/layers/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.client.renderer.entity.layers; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/package-info.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/package-info.java new file mode 100644 index 0000000..f86baed --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.client.renderer.entity; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java b/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java index d48da85..9daf3ed 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java @@ -528,6 +528,11 @@ public class BestiaryInfo { return rangedDamage( damage ).rangedSpread( spread ).rangedWalkSpeed( walkSpeed ).rangedCooldown( cooldown ).rangedMaxRange( range ); } + /** Sets the species ranged attack stats (for a throwing item user). */ + public Builder throwAttack( double spread, double walkSpeed, int cooldown, double range ) { + return rangedSpread( spread ).rangedWalkSpeed( walkSpeed ).rangedCooldown( cooldown ).rangedMaxRange( range ); + } + /** Sets the species ranged attack stats (for a fireball shooter). */ public Builder fireballAttack( double spread, int charge, int cooldown, double range ) { return rangedSpread( spread ).rangedCooldown( charge ).rangedMaxCooldown( charge + cooldown ).rangedMaxRange( range ); diff --git a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/RainfallEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/RainfallEnvironment.java index 71504d9..d833d4b 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/RainfallEnvironment.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/RainfallEnvironment.java @@ -26,6 +26,8 @@ public class RainfallEnvironment extends CompareFloatEnvironment { // Handle the special case of no rainfall if( COMPARATOR == ComparisonOperator.EQUAL_TO && VALUE == 0.0F ) return pos != null && world.getBiome( pos ).getPrecipitation() == Biome.RainType.NONE; + if( COMPARATOR == ComparisonOperator.NOT_EQUAL_TO && VALUE == 0.0F ) + return pos != null && world.getBiome( pos ).getPrecipitation() != Biome.RainType.NONE; return super.matches( world, pos ); } diff --git a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java index 3752895..1c1a7d8 100644 --- a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java +++ b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java @@ -33,10 +33,9 @@ public class SpecialMobs { /* Feature List: //TODO; list may not be complete * (KEY: - = complete in current version, o = incomplete feature from previous version, * + = incomplete new feature, ? = feature to consider adding) - * o general + * - general * - entity replacer - * o dimension-sensitive configs - * o environment-sensitive configs + * - environment-sensitive configs * + natural spawning * o nether spawns * o end spawns @@ -53,7 +52,7 @@ public class SpecialMobs { * - creepers * - chance to spawn charged during thunderstorms * + scope - * - zombies TODO zombie villager renderer + * - zombies * o villager infection * + transformations * - ranged attack AI (using bow) @@ -83,7 +82,7 @@ public class SpecialMobs { * + ranged attack AI (spitter) * + puffer * - endermen - * - witches TODO inject ranged attack stats + * - witches * - ability to equip held items * - uses splash speed instead of regular * - ghasts diff --git a/src/main/java/fathertoast/specialmobs/common/entity/SpecialMobData.java b/src/main/java/fathertoast/specialmobs/common/entity/SpecialMobData.java index e511d5a..9d2fa8b 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/SpecialMobData.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/SpecialMobData.java @@ -1,6 +1,5 @@ package fathertoast.specialmobs.common.entity; -import fathertoast.specialmobs.common.bestiary.BestiaryInfo; import fathertoast.specialmobs.common.bestiary.MobFamily; import fathertoast.specialmobs.common.config.Config; import fathertoast.specialmobs.common.config.species.SpeciesConfig; @@ -21,6 +20,7 @@ import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.living.PotionEvent; import net.minecraftforge.registries.ForgeRegistries; +import javax.annotation.Nullable; import java.util.Collection; import java.util.HashSet; @@ -50,14 +50,14 @@ public class SpecialMobData> { /** Data manager parameter for render scale. */ private final DataParameter renderScale; - /** The base texture of the entity. */ - private ResourceLocation texture; - /** The glowing eyes texture of the entity. */ - private ResourceLocation textureEyes; - /** The overlay texture of the entity. */ - private ResourceLocation textureOverlay; - /** True if the textures need to be sent to the client. */ - private boolean updateTextures; + // /** The base texture of the entity. */ + // private ResourceLocation texture; + // /** The glowing eyes texture of the entity. */ + // private ResourceLocation textureEyes; + // /** The overlay texture of the entity. */ + // private ResourceLocation textureOverlay; + // /** True if the textures need to be sent to the client. */ + // private boolean updateTextures; /** The rate this mob regenerates health (ticks per 1 health). Off if 0 or less. */ private int healTimeMax; @@ -132,10 +132,10 @@ public class SpecialMobData> { } public void initialize() { - final BestiaryInfo info = theEntity.getSpecies().bestiaryInfo; - texture = info.texture; - textureEyes = info.eyesTexture; - textureOverlay = info.overlayTexture; + // final BestiaryInfo info = theEntity.getSpecies().bestiaryInfo; + // texture = info.texture; + // textureEyes = info.eyesTexture; + // textureOverlay = info.overlayTexture; final SpeciesConfig.General config = theEntity.getSpecies().config.GENERAL; theEntity.setExperience( config.experience.get() ); @@ -152,29 +152,29 @@ public class SpecialMobData> { addPotionImmunity( config.immuneToPotions.get().getEntries() ); } - /** Copies all of the data from another mob, optionally copying texture(s). */ - public void copyDataFrom( LivingEntity entity, boolean copyTextures ) { - if( entity instanceof ISpecialMob ) { - CompoundNBT tag = new CompoundNBT(); - - ((ISpecialMob) entity).getSpecialData().writeToNBT( tag ); - if( !copyTextures ) { - tag.remove( TAG_TEXTURE ); - tag.remove( TAG_TEXTURE_EYES ); - tag.remove( TAG_TEXTURE_OVER ); - } - readFromNBT( tag ); - } - } + // /** Copies all of the data from another mob, optionally copying texture(s). */ + // public void copyDataFrom( LivingEntity entity, boolean copyTextures ) { + // if( entity instanceof ISpecialMob ) { + // CompoundNBT tag = new CompoundNBT(); + // + // ((ISpecialMob) entity).getSpecialData().writeToNBT( tag ); + // if( !copyTextures ) { + // tag.remove( TAG_TEXTURE ); + // tag.remove( TAG_TEXTURE_EYES ); + // tag.remove( TAG_TEXTURE_OVER ); + // } + // readFromNBT( tag ); + // } + // } /** Called each tick for every living special mob. */ public void tick() { if( !theEntity.level.isClientSide && theEntity.isAlive() ) { // Send texture to client - if( updateTextures && theEntity.tickCount > 1 ) { - updateTextures = false; - //SpecialMobs.network().sendToDimension( new MessageTexture( theEntity ), theEntity.dimension ); TODO - } + // if( updateTextures && theEntity.tickCount > 1 ) { + // updateTextures = false; + // SpecialMobs.network().sendToDimension( new MessageTexture( theEntity ), theEntity.dimension ); TODO + // } // Update natural regen if( healTimeMax > 0 && ++healTime >= healTimeMax ) { @@ -184,89 +184,85 @@ public class SpecialMobData> { } } - /** @return Whether this entity has a glowing eyes texture. */ - public boolean hasEyesTexture() { return textureEyes != null; } - - /** @return Whether this entity has an overlay texture. */ - public boolean hasOverlayTexture() { return textureOverlay != null; } - /** @return The base texture for the entity. */ - public ResourceLocation getTexture() { return texture; } + public ResourceLocation getTexture() { return theEntity.getSpecies().bestiaryInfo.texture; } /** @return The glowing eyes texture for the entity. */ - public ResourceLocation getTextureEyes() { return textureEyes; } + @Nullable + public ResourceLocation getTextureEyes() { return theEntity.getSpecies().bestiaryInfo.eyesTexture; } /** @return The overlay texture for the entity. */ - public ResourceLocation getTextureOverlay() { return textureOverlay; } + @Nullable + public ResourceLocation getTextureOverlay() { return theEntity.getSpecies().bestiaryInfo.overlayTexture; } - /** @param textures The new texture(s) to set for the entity. */ - private void setTextures( ResourceLocation[] textures ) { - texture = textures[0]; - textureEyes = textures.length > 1 ? textures[1] : null; - textureOverlay = textures.length > 2 ? textures[2] : null; - } + // /** @param textures The new texture(s) to set for the entity. */ + // private void setTextures( ResourceLocation[] textures ) { + // texture = textures[0]; + // textureEyes = textures.length > 1 ? textures[1] : null; + // textureOverlay = textures.length > 2 ? textures[2] : null; + // } - /** @param textures The new texture(s) to load for the entity. Called when loaded from a packet. */ - public void loadTextures( String[] textures ) { - try { - loadTexture( textures[0] ); - loadTextureEyes( textures.length > 1 ? textures[1] : "" ); - loadTextureOverlay( textures.length > 2 ? textures[2] : "" ); - } - catch( Exception ex ) { - SpecialMobs.LOG.warn( "Failed to load textures for {}! ({})", theEntity, textures ); - ex.printStackTrace(); - } - } + // /** @param textures The new texture(s) to load for the entity. Called when loaded from a packet. */ + // public void loadTextures( String[] textures ) { + // try { + // loadTexture( textures[0] ); + // loadTextureEyes( textures.length > 1 ? textures[1] : "" ); + // loadTextureOverlay( textures.length > 2 ? textures[2] : "" ); + // } + // catch( Exception ex ) { + // SpecialMobs.LOG.warn( "Failed to load textures for {}! ({})", theEntity, textures ); + // ex.printStackTrace(); + // } + // } - private void loadTexture( String tex ) { - if( tex.isEmpty() ) throw new IllegalArgumentException( "Entity must have a base texture" ); - final ResourceLocation newTexture = new ResourceLocation( tex ); - if( !newTexture.toString().equals( texture.toString() ) ) { - texture = newTexture; - updateTextures = true; - } - } + // private void loadTexture( String tex ) { + // if( tex.isEmpty() ) throw new IllegalArgumentException( "Entity must have a base texture" ); + // final ResourceLocation newTexture = new ResourceLocation( tex ); + // if( !newTexture.toString().equals( texture.toString() ) ) { + // texture = newTexture; + // updateTextures = true; + // } + // } - private void loadTextureEyes( String tex ) { - if( tex.isEmpty() ) { - if( textureEyes != null ) { - textureEyes = null; - updateTextures = true; - } - } - else if( textureEyes == null ) { - textureEyes = new ResourceLocation( tex ); - updateTextures = true; - } - else { - final ResourceLocation newTexture = new ResourceLocation( tex ); - if( !newTexture.toString().equals( textureEyes.toString() ) ) { - texture = newTexture; - updateTextures = true; - } - } - } + // private void loadTextureEyes( String tex ) { + // if( tex.isEmpty() ) { + // if( textureEyes != null ) { + // textureEyes = null; + // updateTextures = true; + // } + // } + // else if( textureEyes == null ) { + // textureEyes = new ResourceLocation( tex ); + // updateTextures = true; + // } + // else { + // final ResourceLocation newTexture = new ResourceLocation( tex ); + // if( !newTexture.toString().equals( textureEyes.toString() ) ) { + // texture = newTexture; + // updateTextures = true; + // } + // } + // } - private void loadTextureOverlay( String tex ) { - if( tex.isEmpty() ) { - if( textureOverlay != null ) { - textureOverlay = null; - updateTextures = true; - } - } - else if( textureOverlay == null ) { - textureOverlay = new ResourceLocation( tex ); - updateTextures = true; - } - else { - final ResourceLocation newTexture = new ResourceLocation( tex ); - if( !newTexture.toString().equals( textureOverlay.toString() ) ) { - texture = newTexture; - updateTextures = true; - } - } - } + // private void loadTextureOverlay( String tex ) { + // if( tex.isEmpty() ) { + // if( textureOverlay != null ) { + // textureOverlay = null; + // updateTextures = true; + // } + // } + // else if( textureOverlay == null ) { + // textureOverlay = new ResourceLocation( tex ); + // updateTextures = true; + // } + // else { + // final ResourceLocation newTexture = new ResourceLocation( tex ); + // if( !newTexture.toString().equals( textureOverlay.toString() ) ) { + // texture = newTexture; + // updateTextures = true; + // } + // } + // } /** @return The render scale for the entity, including any applied random scaling. */ public float getRenderScale() { return theEntity.getEntityData().get( renderScale ); } @@ -428,9 +424,9 @@ public class SpecialMobData> { public void writeToNBT( CompoundNBT tag ) { tag.putFloat( TAG_RENDER_SCALE, getRenderScale() ); - tag.putString( TAG_TEXTURE, texture.toString() ); - tag.putString( TAG_TEXTURE_EYES, textureEyes == null ? "" : textureEyes.toString() ); - tag.putString( TAG_TEXTURE_OVER, textureOverlay == null ? "" : textureOverlay.toString() ); + // tag.putString( TAG_TEXTURE, texture.toString() ); + // tag.putString( TAG_TEXTURE_EYES, textureEyes == null ? "" : textureEyes.toString() ); + // tag.putString( TAG_TEXTURE_OVER, textureOverlay == null ? "" : textureOverlay.toString() ); // Capabilities tag.putInt( TAG_EXPERIENCE, theEntity.getExperience() ); @@ -483,20 +479,20 @@ public class SpecialMobData> { setRenderScale( tag.getFloat( TAG_RENDER_SCALE ) ); } - try { - if( tag.contains( TAG_TEXTURE, NBT_TYPE_STRING ) ) { - loadTexture( tag.getString( TAG_TEXTURE ) ); - } - if( tag.contains( TAG_TEXTURE_EYES, NBT_TYPE_STRING ) ) { - loadTextureEyes( tag.getString( TAG_TEXTURE_EYES ) ); - } - if( tag.contains( TAG_TEXTURE_OVER, NBT_TYPE_STRING ) ) { - loadTextureOverlay( tag.getString( TAG_TEXTURE_OVER ) ); - } - } - catch( Exception ex ) { - SpecialMobs.LOG.warn( "Failed to load textures from NBT! " + theEntity.toString() ); - } + // try { + // if( tag.contains( TAG_TEXTURE, NBT_TYPE_STRING ) ) { + // loadTexture( tag.getString( TAG_TEXTURE ) ); + // } + // if( tag.contains( TAG_TEXTURE_EYES, NBT_TYPE_STRING ) ) { + // loadTextureEyes( tag.getString( TAG_TEXTURE_EYES ) ); + // } + // if( tag.contains( TAG_TEXTURE_OVER, NBT_TYPE_STRING ) ) { + // loadTextureOverlay( tag.getString( TAG_TEXTURE_OVER ) ); + // } + // } + // catch( Exception ex ) { + // SpecialMobs.LOG.warn( "Failed to load textures from NBT! " + theEntity.toString() ); + // } // Capabilities if( tag.contains( TAG_EXPERIENCE, NBT_TYPE_NUMERICAL ) ) { diff --git a/src/main/java/fathertoast/specialmobs/common/entity/ai/AIHelper.java b/src/main/java/fathertoast/specialmobs/common/entity/ai/AIHelper.java index 13e0255..7a9e70d 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/ai/AIHelper.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/ai/AIHelper.java @@ -11,6 +11,7 @@ import java.util.ArrayList; /** * Provides helper methods for adjusting pre-defined AI goals. */ +@SuppressWarnings( "unused" ) public final class AIHelper { /** Inserts an AI goal at the specified priority. Existing goals have their priority incremented accordingly. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/witch/_SpecialWitchEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/witch/_SpecialWitchEntity.java index f9bfcd5..ebf61f3 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/witch/_SpecialWitchEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/witch/_SpecialWitchEntity.java @@ -7,6 +7,7 @@ import fathertoast.specialmobs.common.core.SpecialMobs; import fathertoast.specialmobs.common.entity.ISpecialMob; import fathertoast.specialmobs.common.entity.MobHelper; import fathertoast.specialmobs.common.entity.SpecialMobData; +import fathertoast.specialmobs.common.entity.ai.AIHelper; import fathertoast.specialmobs.common.util.References; import fathertoast.specialmobs.datagen.loot.LootTableBuilder; import net.minecraft.block.BlockState; @@ -15,6 +16,7 @@ import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.entity.ai.attributes.AttributeModifierMap; import net.minecraft.entity.ai.attributes.Attributes; import net.minecraft.entity.ai.attributes.ModifiableAttributeInstance; +import net.minecraft.entity.ai.goal.RangedAttackGoal; import net.minecraft.entity.monster.AbstractRaiderEntity; import net.minecraft.entity.monster.WitchEntity; import net.minecraft.entity.player.PlayerEntity; @@ -55,7 +57,8 @@ public class _SpecialWitchEntity extends WitchEntity implements ISpecialMob<_Spe public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { bestiaryInfo.color( 0xA80E0E ) .vanillaTextureBaseOnly( "textures/entity/witch.png" ) - .experience( 5 ); + .experience( 5 ) + .throwAttack( 1.0, 1.0, 60, 10.0 ); } @SpecialMob.AttributeSupplier @@ -82,7 +85,6 @@ public class _SpecialWitchEntity extends WitchEntity implements ISpecialMob<_Spe @Override protected void registerGoals() { super.registerGoals(); - //TODO reapply vanilla attack AI to use SMD ranged attack stats registerVariantGoals(); } @@ -90,6 +92,7 @@ public class _SpecialWitchEntity extends WitchEntity implements ISpecialMob<_Spe protected void registerVariantGoals() { } /** Override to change starting equipment or stats. */ + @SuppressWarnings( "unused" ) public void finalizeVariantSpawn( IServerWorld world, DifficultyInstance difficulty, @Nullable SpawnReason spawnReason, @Nullable ILivingEntityData groupData ) { } @@ -101,7 +104,7 @@ public class _SpecialWitchEntity extends WitchEntity implements ISpecialMob<_Spe } /** Override to apply effects when this entity hits a target with a melee attack. */ - protected void onVariantAttack( Entity target ) { } + protected void onVariantAttack( @SuppressWarnings( "unused" ) Entity target ) { } /** Called to attack the target with a ranged attack. */ @Override @@ -120,7 +123,7 @@ public class _SpecialWitchEntity extends WitchEntity implements ISpecialMob<_Spe final PotionEntity thrownPotion = new PotionEntity( level, this ); thrownPotion.setItem( potion ); thrownPotion.xRot += 20.0F; - thrownPotion.shoot( dX, dY + (double) (dH * 0.2F), dZ, 0.75F, 8.0F ); + thrownPotion.shoot( dX, dY + (double) (dH * 0.2F), dZ, 0.75F, 8.0F * getSpecialData().getRangedAttackSpread() ); if( !isSilent() ) { level.playSound( null, getX(), getY(), getZ(), SoundEvents.WITCH_THROW, getSoundSource(), 1.0F, 0.8F + random.nextFloat() * 0.4F ); @@ -170,6 +173,7 @@ public class _SpecialWitchEntity extends WitchEntity implements ISpecialMob<_Spe } /** Override to modify potion support. Return an empty item stack to cancel the potion throw. */ + @SuppressWarnings( "unused" ) protected ItemStack pickVariantSupportPotion( ItemStack originalPotion, AbstractRaiderEntity target, float distance ) { return originalPotion; } @@ -234,6 +238,8 @@ public class _SpecialWitchEntity extends WitchEntity implements ISpecialMob<_Spe public _SpecialWitchEntity( EntityType entityType, World world ) { super( entityType, world ); usingTime = Integer.MAX_VALUE; // Effectively disable vanilla witch potion drinking logic in combo with "fake drinking" + recalculateAttackGoal(); + getSpecialData().initialize(); } @@ -244,6 +250,15 @@ public class _SpecialWitchEntity extends WitchEntity implements ISpecialMob<_Spe specialData = new SpecialMobData<>( this, SCALE ); } + /** Called to update this entity's attack AI based on NBT data. */ + public void recalculateAttackGoal() { + if( level != null && !level.isClientSide ) { + AIHelper.removeGoals( goalSelector, RangedAttackGoal.class ); + goalSelector.addGoal( 2, new RangedAttackGoal( this, getSpecialData().getRangedWalkSpeed(), + getSpecialData().getRangedAttackCooldown(), getSpecialData().getRangedAttackMaxRange() ) ); + } + } + /** Called each AI tick to update potion-drinking behavior. */ public void drinkPotionUpdate() { potionUseCooldownTimer--; @@ -327,6 +342,7 @@ public class _SpecialWitchEntity extends WitchEntity implements ISpecialMob<_Spe public ItemStack makePotion( Potion type ) { return newPotion( Items.POTION, type ); } /** @return A new regular potion with custom effects. */ + @SuppressWarnings( "unused" ) public ItemStack makePotion( Collection effects ) { return newPotion( Items.POTION, effects ); } /** @return A new splash potion with standard effects. */ @@ -339,6 +355,7 @@ public class _SpecialWitchEntity extends WitchEntity implements ISpecialMob<_Spe public ItemStack makeLingeringPotion( Potion type ) { return newPotion( Items.LINGERING_POTION, type ); } /** @return A new lingering splash potion with custom effects. */ + @SuppressWarnings( "unused" ) public ItemStack makeLingeringPotion( Collection effects ) { return newPotion( Items.LINGERING_POTION, effects ); } /** @return A new potion with standard effects. */ @@ -509,5 +526,7 @@ public class _SpecialWitchEntity extends WitchEntity implements ISpecialMob<_Spe getSpecialData().readFromNBT( saveTag ); readVariantSaveData( saveTag ); + + recalculateAttackGoal(); } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombie/MadScientistZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/MadScientistZombieEntity.java index 78df96d..9f2e7fd 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/zombie/MadScientistZombieEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombie/MadScientistZombieEntity.java @@ -36,7 +36,7 @@ public class MadScientistZombieEntity extends _SpecialZombieEntity implements IA @SpecialMob.BestiaryInfoSupplier public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { - bestiaryInfo.color( 0xDED4C6 ) + bestiaryInfo.color( 0xDED4C6 ).weight( BestiaryInfo.DefaultWeight.LOW ) .uniqueTextureBaseOnly() .addExperience( 2 ).disableRangedAttack(); } diff --git a/src/main/java/fathertoast/specialmobs/datagen/SMLanguageProvider.java b/src/main/java/fathertoast/specialmobs/datagen/SMLanguageProvider.java index 47a00a2..f3c16d5 100644 --- a/src/main/java/fathertoast/specialmobs/datagen/SMLanguageProvider.java +++ b/src/main/java/fathertoast/specialmobs/datagen/SMLanguageProvider.java @@ -45,29 +45,32 @@ public class SMLanguageProvider extends LanguageProvider { final String[] spawnEggTranslationPattern = References.translations( "%s", "%s Spawn Egg", "%s", "%s", "%s", "%s", "%s", "%s" ); //TODO + final String[] vanillaReplacementSpawnEggTranslationPattern = References.translations( "%s", "Normal %s Spawn Egg", + "%s", "%s", "%s", "%s", "%s", "%s" ); //TODO // Bestiary-generated translations for( MobFamily.Species species : MobFamily.getAllSpecies() ) { final String[] speciesTranslations = AnnotationHelper.getTranslations( species ); - String[] spawnEggTranslations = format( spawnEggTranslationPattern, speciesTranslations ); + String[] spawnEggTranslations = format( species.specialVariantName == null ? + vanillaReplacementSpawnEggTranslationPattern : spawnEggTranslationPattern, speciesTranslations ); spawnEggTranslations[0] = species.spawnEgg.get().getDescriptionId(); translationList.add( speciesTranslations ); translationList.add( spawnEggTranslations ); } - + // Items - for (RegistryObject regObject : SMItems.REGISTRY.getEntries()) { + for( RegistryObject regObject : SMItems.REGISTRY.getEntries() ) { Item item = regObject.get(); - + // Lazy method of avoiding duplicate entries for now - if (item instanceof ForgeSpawnEggItem) { + if( item instanceof ForgeSpawnEggItem ) { continue; } - final String[] itemTranslations = AnnotationHelper.getTranslations(regObject.get()); - translationList.add(itemTranslations); + final String[] itemTranslations = AnnotationHelper.getTranslations( regObject.get() ); + translationList.add( itemTranslations ); } - + TRANSLATIONS = translationList.toArray( new String[0][0] ); // Assign all specific locales to the translation we want to use From a20b87a2416f9846f8985761075cb091253783de Mon Sep 17 00:00:00 2001 From: FatherToast Date: Thu, 21 Jul 2022 19:38:25 -0500 Subject: [PATCH 13/16] bit more cleanup and fixes --- .../common/compat/top/package-info.java | 7 +++++ .../common/config/package-info.java | 7 +++++ .../common/entity/ai/FluidPathNavigator.java | 5 ---- .../ai/SimpleFlyingMovementController.java | 5 ---- .../entity/ai/goal/ChargeCreeperGoal.java | 1 + .../ai/goal/SpecialLeapAtTargetGoal.java | 3 +++ .../common/entity/ai/package-info.java | 7 +++++ .../entity/blaze/_SpecialBlazeEntity.java | 1 + .../cavespider/_SpecialCaveSpiderEntity.java | 1 + .../entity/creeper/_SpecialCreeperEntity.java | 1 + .../enderman/_SpecialEndermanEntity.java | 1 + .../entity/ghast/_SpecialGhastEntity.java | 1 + .../magmacube/_SpecialMagmaCubeEntity.java | 1 + .../silverfish/_SpecialSilverfishEntity.java | 5 ++-- .../entity/silverfish/package-info.java | 7 +++++ .../skeleton/_SpecialSkeletonEntity.java | 4 +-- .../entity/slime/_SpecialSlimeEntity.java | 3 ++- .../entity/spider/_SpecialSpiderEntity.java | 1 + .../entity/witch/_SpecialWitchEntity.java | 2 +- .../_SpecialWitherSkeletonEntity.java | 4 +-- .../_SpecialZombifiedPiglinEntity.java | 4 +-- .../common/event/package-info.java | 7 +++++ .../specialmobs/common/item/package-info.java | 7 +++++ .../common/network/message/package-info.java | 7 +++++ .../common/network/package-info.java | 7 +++++ .../common/network/work/package-info.java | 7 +++++ .../common/util/AnnotationHelper.java | 4 --- .../specialmobs/common/util/EntityUtil.java | 27 +++++++++---------- .../common/util/ExplosionHelper.java | 5 +--- .../specialmobs/common/util/References.java | 6 ++--- .../specialmobs/common/util/package-info.java | 7 +++++ .../datagen/SMLootTableProvider.java | 4 --- .../datagen/loot/LootEntryItemBuilder.java | 16 +---------- .../specialmobs/datagen/loot/LootHelper.java | 2 -- .../datagen/loot/LootPoolBuilder.java | 3 +-- .../datagen/loot/package-info.java | 7 +++++ .../specialmobs/datagen/package-info.java | 7 +++++ 37 files changed, 126 insertions(+), 68 deletions(-) create mode 100644 src/main/java/fathertoast/specialmobs/common/compat/top/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/config/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/ai/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/event/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/item/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/network/message/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/network/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/network/work/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/common/util/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/datagen/loot/package-info.java create mode 100644 src/main/java/fathertoast/specialmobs/datagen/package-info.java diff --git a/src/main/java/fathertoast/specialmobs/common/compat/top/package-info.java b/src/main/java/fathertoast/specialmobs/common/compat/top/package-info.java new file mode 100644 index 0000000..46cc2c7 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/compat/top/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.compat.top; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/config/package-info.java b/src/main/java/fathertoast/specialmobs/common/config/package-info.java new file mode 100644 index 0000000..ad2c3d5 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.config; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/ai/FluidPathNavigator.java b/src/main/java/fathertoast/specialmobs/common/entity/ai/FluidPathNavigator.java index 6a9020a..4262cb8 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/ai/FluidPathNavigator.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/ai/FluidPathNavigator.java @@ -1,6 +1,5 @@ package fathertoast.specialmobs.common.entity.ai; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.entity.MobEntity; @@ -11,13 +10,9 @@ import net.minecraft.pathfinding.WalkNodeProcessor; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -import javax.annotation.ParametersAreNonnullByDefault; - /** * A path navigator used for entities that can walk on fluids. Based on the strider's lava path navigator. */ -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault public class FluidPathNavigator extends GroundPathNavigator { private final boolean waterWalkable; diff --git a/src/main/java/fathertoast/specialmobs/common/entity/ai/SimpleFlyingMovementController.java b/src/main/java/fathertoast/specialmobs/common/entity/ai/SimpleFlyingMovementController.java index 288f806..9a4715a 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/ai/SimpleFlyingMovementController.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/ai/SimpleFlyingMovementController.java @@ -1,6 +1,5 @@ package fathertoast.specialmobs.common.entity.ai; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.MobEntity; import net.minecraft.entity.ai.attributes.Attributes; @@ -9,13 +8,9 @@ import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.vector.Vector3d; -import javax.annotation.ParametersAreNonnullByDefault; - /** * Simple movement controller that can be used by flying entities, which takes their movement speed attribute into account. */ -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault public class SimpleFlyingMovementController extends MovementController { private int floatDuration; diff --git a/src/main/java/fathertoast/specialmobs/common/entity/ai/goal/ChargeCreeperGoal.java b/src/main/java/fathertoast/specialmobs/common/entity/ai/goal/ChargeCreeperGoal.java index 967fded..1893281 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/ai/goal/ChargeCreeperGoal.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/ai/goal/ChargeCreeperGoal.java @@ -38,6 +38,7 @@ public class ChargeCreeperGoal extends Goal { } /** Builder that enables the entity to leap while mounted. */ + @SuppressWarnings( "unused" ) public ChargeCreeperGoal canUseWhileMounted() { canUseWhileMounted = true; return this; diff --git a/src/main/java/fathertoast/specialmobs/common/entity/ai/goal/SpecialLeapAtTargetGoal.java b/src/main/java/fathertoast/specialmobs/common/entity/ai/goal/SpecialLeapAtTargetGoal.java index 735715e..7a90262 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/ai/goal/SpecialLeapAtTargetGoal.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/ai/goal/SpecialLeapAtTargetGoal.java @@ -37,18 +37,21 @@ public class SpecialLeapAtTargetGoal extends Goal { } /** Builder that sets momentum. */ + @SuppressWarnings( "unused" ) public SpecialLeapAtTargetGoal setMomentum( float value ) { momentum = value; return this; } /** Builder that enables the entity to leap while mounted. */ + @SuppressWarnings( "unused" ) public SpecialLeapAtTargetGoal canUseWhileMounted() { canUseWhileMounted = true; return this; } /** Builder that enables the entity to ignore fall damage from leaping. */ + @SuppressWarnings( "unused" ) public SpecialLeapAtTargetGoal ignoreFallDamage() { ignoreFallDamage = true; return this; diff --git a/src/main/java/fathertoast/specialmobs/common/entity/ai/package-info.java b/src/main/java/fathertoast/specialmobs/common/entity/ai/package-info.java new file mode 100644 index 0000000..ad734d9 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/ai/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.entity.ai; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/blaze/_SpecialBlazeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/blaze/_SpecialBlazeEntity.java index 3f96ecf..26fbc74 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/blaze/_SpecialBlazeEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/blaze/_SpecialBlazeEntity.java @@ -93,6 +93,7 @@ public class _SpecialBlazeEntity extends BlazeEntity implements IRangedAttackMob protected void registerVariantGoals() { } /** Override to change starting equipment or stats. */ + @SuppressWarnings( "unused" ) public void finalizeVariantSpawn( IServerWorld world, DifficultyInstance difficulty, @Nullable SpawnReason spawnReason, @Nullable ILivingEntityData groupData ) { } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java index 2e99e7b..398963c 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java @@ -86,6 +86,7 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci protected void registerVariantGoals() { } /** Override to change starting equipment or stats. */ + @SuppressWarnings( "unused" ) public void finalizeVariantSpawn( IServerWorld world, DifficultyInstance difficulty, @Nullable SpawnReason spawnReason, @Nullable ILivingEntityData groupData ) { } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java index 49d9d37..29804ad 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java @@ -90,6 +90,7 @@ public class _SpecialCreeperEntity extends CreeperEntity implements IExplodingMo protected void registerVariantGoals() { } /** Override to change starting equipment or stats. */ + @SuppressWarnings( "unused" ) public void finalizeVariantSpawn( IServerWorld world, DifficultyInstance difficulty, @Nullable SpawnReason spawnReason, @Nullable ILivingEntityData groupData ) { } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/enderman/_SpecialEndermanEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/enderman/_SpecialEndermanEntity.java index 8d32379..7393e75 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/enderman/_SpecialEndermanEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/enderman/_SpecialEndermanEntity.java @@ -71,6 +71,7 @@ public class _SpecialEndermanEntity extends EndermanEntity implements ISpecialMo protected void registerVariantGoals() { } /** Override to change starting equipment or stats. */ + @SuppressWarnings( "unused" ) public void finalizeVariantSpawn( IServerWorld world, DifficultyInstance difficulty, @Nullable SpawnReason spawnReason, @Nullable ILivingEntityData groupData ) { } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/ghast/_SpecialGhastEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/ghast/_SpecialGhastEntity.java index fbf1db8..a7bd033 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/ghast/_SpecialGhastEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/ghast/_SpecialGhastEntity.java @@ -99,6 +99,7 @@ public class _SpecialGhastEntity extends GhastEntity implements IRangedAttackMob protected int getVariantAttackPriority() { return 4; } /** Override to change starting equipment or stats. */ + @SuppressWarnings( "unused" ) public void finalizeVariantSpawn( IServerWorld world, DifficultyInstance difficulty, @Nullable SpawnReason spawnReason, @Nullable ILivingEntityData groupData ) { } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/magmacube/_SpecialMagmaCubeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/magmacube/_SpecialMagmaCubeEntity.java index 2e9be01..982899f 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/magmacube/_SpecialMagmaCubeEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/magmacube/_SpecialMagmaCubeEntity.java @@ -77,6 +77,7 @@ public class _SpecialMagmaCubeEntity extends MagmaCubeEntity implements ISpecial protected void registerVariantGoals() { } /** Override to change starting equipment or stats. */ + @SuppressWarnings( "unused" ) public void finalizeVariantSpawn( IServerWorld world, DifficultyInstance difficulty, @Nullable SpawnReason spawnReason, @Nullable ILivingEntityData groupData ) { } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/_SpecialSilverfishEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/_SpecialSilverfishEntity.java index 0ebdf87..004cb47 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/_SpecialSilverfishEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/_SpecialSilverfishEntity.java @@ -92,6 +92,7 @@ public class _SpecialSilverfishEntity extends SilverfishEntity implements ISpeci protected void registerVariantGoals() { } /** Override to change starting equipment or stats. */ + @SuppressWarnings( "unused" ) public void finalizeVariantSpawn( IServerWorld world, DifficultyInstance difficulty, @Nullable SpawnReason spawnReason, @Nullable ILivingEntityData groupData ) { } @@ -106,10 +107,10 @@ public class _SpecialSilverfishEntity extends SilverfishEntity implements ISpeci protected void onVariantAttack( Entity target ) { } /** Override to save data to this entity's NBT data. */ - public void addVariantSaveData( CompoundNBT saveTag ) { } + public void addVariantSaveData( @SuppressWarnings( "unused" ) CompoundNBT saveTag ) { } /** Override to load data from this entity's NBT data. */ - public void readVariantSaveData( CompoundNBT saveTag ) { } + public void readVariantSaveData( @SuppressWarnings( "unused" ) CompoundNBT saveTag ) { } //--------------- Family-Specific Implementations ---------------- diff --git a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/package-info.java b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/package-info.java index e69de29..1839db5 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/package-info.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.entity.silverfish; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/_SpecialSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/_SpecialSkeletonEntity.java index 1ad9421..aa2f086 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/_SpecialSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/_SpecialSkeletonEntity.java @@ -152,10 +152,10 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS protected void onVariantAttack( Entity target ) { } /** Override to save data to this entity's NBT data. */ - public void addVariantSaveData( CompoundNBT saveTag ) { } + public void addVariantSaveData( @SuppressWarnings( "unused" ) CompoundNBT saveTag ) { } /** Override to load data from this entity's NBT data. */ - public void readVariantSaveData( CompoundNBT saveTag ) { } + public void readVariantSaveData( @SuppressWarnings( "unused" ) CompoundNBT saveTag ) { } //--------------- Family-Specific Implementations ---------------- diff --git a/src/main/java/fathertoast/specialmobs/common/entity/slime/_SpecialSlimeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/slime/_SpecialSlimeEntity.java index e729d11..e557d64 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/slime/_SpecialSlimeEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/slime/_SpecialSlimeEntity.java @@ -40,7 +40,7 @@ public class _SpecialSlimeEntity extends SlimeEntity implements ISpecialMob<_Spe @SpecialMob.BestiaryInfoSupplier public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { - bestiaryInfo.color( 0x51A03E ) + bestiaryInfo.color( 0x7EBF6E ) .vanillaTextureBaseOnly( "textures/entity/slime/slime.png" ) .experience( 0 ); } @@ -78,6 +78,7 @@ public class _SpecialSlimeEntity extends SlimeEntity implements ISpecialMob<_Spe protected void registerVariantGoals() { } /** Override to change starting equipment or stats. */ + @SuppressWarnings( "unused" ) public void finalizeVariantSpawn( IServerWorld world, DifficultyInstance difficulty, @Nullable SpawnReason spawnReason, @Nullable ILivingEntityData groupData ) { } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/_SpecialSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/_SpecialSpiderEntity.java index c79c625..07a322f 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/spider/_SpecialSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/_SpecialSpiderEntity.java @@ -86,6 +86,7 @@ public class _SpecialSpiderEntity extends SpiderEntity implements ISpecialMob<_S protected void registerVariantGoals() { } /** Override to change starting equipment or stats. */ + @SuppressWarnings( "unused" ) public void finalizeVariantSpawn( IServerWorld world, DifficultyInstance difficulty, @Nullable SpawnReason spawnReason, @Nullable ILivingEntityData groupData ) { } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/witch/_SpecialWitchEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/witch/_SpecialWitchEntity.java index ebf61f3..7da3693 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/witch/_SpecialWitchEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/witch/_SpecialWitchEntity.java @@ -55,7 +55,7 @@ public class _SpecialWitchEntity extends WitchEntity implements ISpecialMob<_Spe @SpecialMob.BestiaryInfoSupplier public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { - bestiaryInfo.color( 0xA80E0E ) + bestiaryInfo.color( 0x51A03E ) .vanillaTextureBaseOnly( "textures/entity/witch.png" ) .experience( 5 ) .throwAttack( 1.0, 1.0, 60, 10.0 ); diff --git a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java index 808e186..2034177 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java @@ -150,10 +150,10 @@ public class _SpecialWitherSkeletonEntity extends WitherSkeletonEntity implement protected void onVariantAttack( Entity target ) { } /** Override to save data to this entity's NBT data. */ - public void addVariantSaveData( CompoundNBT saveTag ) { } + public void addVariantSaveData( @SuppressWarnings( "unused" ) CompoundNBT saveTag ) { } /** Override to load data from this entity's NBT data. */ - public void readVariantSaveData( CompoundNBT saveTag ) { } + public void readVariantSaveData( @SuppressWarnings( "unused" ) CompoundNBT saveTag ) { } //--------------- Family-Specific Implementations ---------------- diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/_SpecialZombifiedPiglinEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/_SpecialZombifiedPiglinEntity.java index 0f8f4ce..e9e3a2d 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/_SpecialZombifiedPiglinEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/_SpecialZombifiedPiglinEntity.java @@ -151,10 +151,10 @@ public class _SpecialZombifiedPiglinEntity extends ZombifiedPiglinEntity impleme protected void onVariantAttack( Entity target ) { } /** Override to save data to this entity's NBT data. */ - public void addVariantSaveData( CompoundNBT saveTag ) { } + public void addVariantSaveData( @SuppressWarnings( "unused" ) CompoundNBT saveTag ) { } /** Override to load data from this entity's NBT data. */ - public void readVariantSaveData( CompoundNBT saveTag ) { } + public void readVariantSaveData( @SuppressWarnings( "unused" ) CompoundNBT saveTag ) { } //--------------- Family-Specific Implementations ---------------- diff --git a/src/main/java/fathertoast/specialmobs/common/event/package-info.java b/src/main/java/fathertoast/specialmobs/common/event/package-info.java new file mode 100644 index 0000000..ea722db --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/event/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.event; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/item/package-info.java b/src/main/java/fathertoast/specialmobs/common/item/package-info.java new file mode 100644 index 0000000..c70f634 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/item/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.item; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/network/message/package-info.java b/src/main/java/fathertoast/specialmobs/common/network/message/package-info.java new file mode 100644 index 0000000..fe94e62 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/network/message/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.network.message; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/network/package-info.java b/src/main/java/fathertoast/specialmobs/common/network/package-info.java new file mode 100644 index 0000000..298c8d9 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/network/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.network; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/network/work/package-info.java b/src/main/java/fathertoast/specialmobs/common/network/work/package-info.java new file mode 100644 index 0000000..0d1f71b --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/network/work/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.network.work; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/util/AnnotationHelper.java b/src/main/java/fathertoast/specialmobs/common/util/AnnotationHelper.java index 616584c..163f5f6 100644 --- a/src/main/java/fathertoast/specialmobs/common/util/AnnotationHelper.java +++ b/src/main/java/fathertoast/specialmobs/common/util/AnnotationHelper.java @@ -5,14 +5,12 @@ import fathertoast.specialmobs.common.bestiary.MobFamily; import fathertoast.specialmobs.common.bestiary.SpecialMob; import fathertoast.specialmobs.common.config.species.SpeciesConfig; import fathertoast.specialmobs.datagen.loot.LootTableBuilder; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.ai.attributes.AttributeModifierMap; import net.minecraft.item.Item; import javax.annotation.Nullable; -import javax.annotation.ParametersAreNonnullByDefault; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -23,8 +21,6 @@ import java.lang.reflect.Modifier; * Provides helper methods to handle annotation processing through reflection. */ @SuppressWarnings( "SameParameterValue" ) -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault public final class AnnotationHelper { //--------------- PRETTY HELPER METHODS ---------------- diff --git a/src/main/java/fathertoast/specialmobs/common/util/EntityUtil.java b/src/main/java/fathertoast/specialmobs/common/util/EntityUtil.java index fa46bf4..621de83 100644 --- a/src/main/java/fathertoast/specialmobs/common/util/EntityUtil.java +++ b/src/main/java/fathertoast/specialmobs/common/util/EntityUtil.java @@ -11,22 +11,21 @@ import net.minecraft.util.math.vector.Vector3d; import javax.annotation.Nullable; public class EntityUtil { - + @Nullable - public static Entity getClientPickEntity(PlayerEntity player, double pickRange) { - if (!player.level.isClientSide) { - SpecialMobs.LOG.error("Tried to fetch player \"mouse-over\" entity from server side. This can't be right?"); + public static Entity getClientPickEntity( PlayerEntity player, double pickRange ) { + if( !player.level.isClientSide ) { + SpecialMobs.LOG.error( "Tried to fetch player \"mouse-over\" entity from server side. This can't be right?" ); return null; } - Vector3d eyePos = player.getEyePosition(1.0F); - Vector3d viewVec = player.getViewVector(1.0F); - Vector3d targetVec = eyePos.add(viewVec.x * pickRange, viewVec.y * pickRange, viewVec.z * pickRange); - - AxisAlignedBB AABB = player.getBoundingBox().expandTowards(viewVec.scale(pickRange)).inflate(1.0D, 1.0D, 1.0D); - EntityRayTraceResult result = ProjectileHelper.getEntityHitResult(player, eyePos, targetVec, AABB, (entity) -> { - return !entity.isSpectator() && entity.isPickable(); - }, pickRange); - + Vector3d eyePos = player.getEyePosition( 1.0F ); + Vector3d viewVec = player.getViewVector( 1.0F ); + Vector3d targetVec = eyePos.add( viewVec.x * pickRange, viewVec.y * pickRange, viewVec.z * pickRange ); + + AxisAlignedBB AABB = player.getBoundingBox().expandTowards( viewVec.scale( pickRange ) ).inflate( 1.0D, 1.0D, 1.0D ); + EntityRayTraceResult result = ProjectileHelper.getEntityHitResult( player, eyePos, targetVec, AABB, + ( entity ) -> !entity.isSpectator() && entity.isPickable(), pickRange ); + return result != null ? result.getEntity() : null; } -} +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/util/ExplosionHelper.java b/src/main/java/fathertoast/specialmobs/common/util/ExplosionHelper.java index 3b3e374..d30ea00 100644 --- a/src/main/java/fathertoast/specialmobs/common/util/ExplosionHelper.java +++ b/src/main/java/fathertoast/specialmobs/common/util/ExplosionHelper.java @@ -1,6 +1,5 @@ package fathertoast.specialmobs.common.util; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.block.BlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; @@ -16,7 +15,6 @@ import net.minecraft.world.World; import net.minecraft.world.server.ServerWorld; import net.minecraftforge.event.ForgeEventFactory; -import javax.annotation.ParametersAreNonnullByDefault; import java.util.List; import java.util.Optional; @@ -25,8 +23,6 @@ import java.util.Optional; * and providing helper methods. */ @SuppressWarnings( "UnusedReturnValue" ) -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault public class ExplosionHelper { /** Gets the explosion mode to use as a result of appropriate Forge events. */ @@ -150,6 +146,7 @@ public class ExplosionHelper { } /** @return The block's explosion resistance (helper method). */ + @SuppressWarnings( "unused" ) public Optional getBlockExplosionResistance( BlockPos pos, BlockState block ) { return damageCalculator.getBlockExplosionResistance( explosion, level, pos, block, level.getFluidState( pos ) ); } diff --git a/src/main/java/fathertoast/specialmobs/common/util/References.java b/src/main/java/fathertoast/specialmobs/common/util/References.java index b079563..8be7fa1 100644 --- a/src/main/java/fathertoast/specialmobs/common/util/References.java +++ b/src/main/java/fathertoast/specialmobs/common/util/References.java @@ -61,9 +61,9 @@ public final class References { public static final String TAG_RENDER_SCALE = "RenderScale"; public static final String TAG_EXPERIENCE = "Experience"; public static final String TAG_REGENERATION = "Regeneration"; - public static final String TAG_TEXTURE = "Texture"; - public static final String TAG_TEXTURE_EYES = "TextureEyes"; - public static final String TAG_TEXTURE_OVER = "TextureOverlay"; + // public static final String TAG_TEXTURE = "Texture"; + // public static final String TAG_TEXTURE_EYES = "TextureEyes"; + // public static final String TAG_TEXTURE_OVER = "TextureOverlay"; public static final String TAG_ARROW_DAMAGE = "ArrowDamage"; public static final String TAG_ARROW_SPREAD = "ArrowSpread"; public static final String TAG_ARROW_WALK_SPEED = "ArrowWalkSpeed"; diff --git a/src/main/java/fathertoast/specialmobs/common/util/package-info.java b/src/main/java/fathertoast/specialmobs/common/util/package-info.java new file mode 100644 index 0000000..08eb3e4 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/util/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.common.util; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/datagen/SMLootTableProvider.java b/src/main/java/fathertoast/specialmobs/datagen/SMLootTableProvider.java index bde510b..7181632 100644 --- a/src/main/java/fathertoast/specialmobs/datagen/SMLootTableProvider.java +++ b/src/main/java/fathertoast/specialmobs/datagen/SMLootTableProvider.java @@ -5,7 +5,6 @@ import com.mojang.datafixers.util.Pair; import fathertoast.specialmobs.common.bestiary.MobFamily; import fathertoast.specialmobs.common.core.register.SMEntities; import fathertoast.specialmobs.common.util.AnnotationHelper; -import mcp.MethodsReturnNonnullByDefault; import net.minecraft.advancements.criterion.EntityPredicate; import net.minecraft.data.DataGenerator; import net.minecraft.data.LootTableProvider; @@ -18,15 +17,12 @@ import net.minecraft.loot.ValidationTracker; import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.RegistryObject; -import javax.annotation.ParametersAreNonnullByDefault; import java.util.List; import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Supplier; -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault public class SMLootTableProvider extends LootTableProvider { public SMLootTableProvider( DataGenerator gen ) { super( gen ); } diff --git a/src/main/java/fathertoast/specialmobs/datagen/loot/LootEntryItemBuilder.java b/src/main/java/fathertoast/specialmobs/datagen/loot/LootEntryItemBuilder.java index 7109978..e3759fd 100644 --- a/src/main/java/fathertoast/specialmobs/datagen/loot/LootEntryItemBuilder.java +++ b/src/main/java/fathertoast/specialmobs/datagen/loot/LootEntryItemBuilder.java @@ -17,8 +17,7 @@ import java.util.ArrayList; import java.util.List; @SuppressWarnings( { "unused", "WeakerAccess", "UnusedReturnValue" } ) -public -class LootEntryItemBuilder { +public class LootEntryItemBuilder { private final IItemProvider item; @@ -37,9 +36,6 @@ class LootEntryItemBuilder { if( itemStack.getTag() != null ) { setNBTTag( itemStack.getTag().copy() ); } - // if( !itemStack.isDamageableItem() && itemStack.getMetadata() != 0 ) { - // setMetadata( itemStack.getMetadata() ); - // } } /** @return A new loot entry object reflecting the current state of this builder. */ @@ -101,16 +97,6 @@ class LootEntryItemBuilder { return addFunction( SetDamage.setDamage( new RandomValueRange( min, max ) ) ); } - // /** Adds a set metadata function. */ - // public LootEntryItemBuilder setMetadata( int min, int max ) { - // return addFunction( new SetMetadata( NO_CONDITIONS, new RandomValueRange( min, max ) ) ); - // } - // - // /** Adds a set metadata function. */ - // public LootEntryItemBuilder setMetadata( int value ) { - // return addFunction( new SetMetadata( NO_CONDITIONS, new RandomValueRange( value ) ) ); - // } - /** Adds an nbt tag compound function. */ public LootEntryItemBuilder setNBTTag( CompoundNBT tag ) { return addFunction( SetNBT.setTag( tag ) ); } diff --git a/src/main/java/fathertoast/specialmobs/datagen/loot/LootHelper.java b/src/main/java/fathertoast/specialmobs/datagen/loot/LootHelper.java index d8ed2ea..5c73c5d 100644 --- a/src/main/java/fathertoast/specialmobs/datagen/loot/LootHelper.java +++ b/src/main/java/fathertoast/specialmobs/datagen/loot/LootHelper.java @@ -6,10 +6,8 @@ import net.minecraft.loot.conditions.KilledByPlayer; import net.minecraft.loot.conditions.RandomChanceWithLooting; import net.minecraft.loot.functions.ILootFunction; -import javax.annotation.ParametersAreNonnullByDefault; import java.util.List; -@ParametersAreNonnullByDefault public class LootHelper { public static final ILootCondition.IBuilder KILLED_BY_PLAYER_CONDITION = KilledByPlayer.killedByPlayer(); diff --git a/src/main/java/fathertoast/specialmobs/datagen/loot/LootPoolBuilder.java b/src/main/java/fathertoast/specialmobs/datagen/loot/LootPoolBuilder.java index 0b30609..39a2413 100644 --- a/src/main/java/fathertoast/specialmobs/datagen/loot/LootPoolBuilder.java +++ b/src/main/java/fathertoast/specialmobs/datagen/loot/LootPoolBuilder.java @@ -10,8 +10,7 @@ import java.util.Arrays; import java.util.List; @SuppressWarnings( { "unused", "WeakerAccess", "UnusedReturnValue" } ) -public -class LootPoolBuilder { +public class LootPoolBuilder { private final String name; diff --git a/src/main/java/fathertoast/specialmobs/datagen/loot/package-info.java b/src/main/java/fathertoast/specialmobs/datagen/loot/package-info.java new file mode 100644 index 0000000..5c78346 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/datagen/loot/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.datagen.loot; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/datagen/package-info.java b/src/main/java/fathertoast/specialmobs/datagen/package-info.java new file mode 100644 index 0000000..cf55109 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/datagen/package-info.java @@ -0,0 +1,7 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package fathertoast.specialmobs.datagen; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file From 2d621f6b1299523923e4a8c0e0f1c3b6ed3dfadb Mon Sep 17 00:00:00 2001 From: FatherToast Date: Fri, 22 Jul 2022 23:08:35 -0500 Subject: [PATCH 14/16] Very WIP new mobs; other stuff too --- .../common/bestiary/BestiaryInfo.java | 11 + .../common/bestiary/MobFamily.java | 26 ++- .../specialmobs/common/core/SpecialMobs.java | 2 +- .../specialmobs/common/entity/MobHelper.java | 14 +- .../blaze/ConflagrationBlazeEntity.java | 3 +- .../entity/creeper/DrowningCreeperEntity.java | 4 + .../creeper/LightningCreeperEntity.java | 2 +- .../entity/creeper/SandCreeperEntity.java | 153 ++++++++++++++ .../entity/creeper/SnowCreeperEntity.java | 193 ++++++++++++++++++ .../entity/enderman/FlameEndermanEntity.java | 63 ++++++ .../entity/enderman/IcyEndermanEntity.java | 5 +- .../enderman/LightningEndermanEntity.java | 4 +- .../entity/enderman/MirageEndermanEntity.java | 2 + .../entity/enderman/RunicEndermanEntity.java | 71 +++++++ .../ghast/CorporealShiftGhastEntity.java | 73 +++---- .../DesiccatedSilverfishEntity.java | 113 ++++++++++ .../silverfish/FishingSilverfishEntity.java | 3 + .../entity/slime/BlueberrySlimeEntity.java | 1 + .../common/entity/slime/LemonSlimeEntity.java | 2 +- .../entity/spider/FireSpiderEntity.java | 68 ++++++ .../entity/spider/HungrySpiderEntity.java | 8 +- .../entity/spider/WaterSpiderEntity.java | 100 +++++++++ .../entity/zombie/FishingZombieEntity.java | 3 + .../zombie/MadScientistZombieEntity.java | 2 +- .../FishingZombifiedPiglinEntity.java | 5 +- .../textures/entity/cave_spider/web_eyes.png | Bin 506 -> 380 bytes .../textures/entity/creeper/sand.png | Bin 0 -> 626 bytes .../textures/entity/creeper/snow.png | Bin 0 -> 3081 bytes .../textures/entity/creeper/splitting.png | Bin 5817 -> 5923 bytes .../entity/creeper/splitting_eyes.png | Bin 2803 -> 2829 bytes .../textures/entity/enderman/flame.png | Bin 0 -> 576 bytes .../textures/entity/enderman/flame_eyes.png | Bin 0 -> 233 bytes .../textures/entity/enderman/runic.png | Bin 0 -> 632 bytes .../textures/entity/enderman/runic_eyes.png | Bin 0 -> 354 bytes .../textures/entity/silverfish/desiccated.png | Bin 0 -> 577 bytes .../silverfish/{fishy.png => puffer.png} | Bin .../textures/entity/spider/desert.png | Bin 2251 -> 987 bytes .../textures/entity/spider/desert_eyes.png | Bin 184 -> 209 bytes .../textures/entity/spider/fire.png | Bin 0 -> 841 bytes .../textures/entity/spider/fire_eyes.png | Bin 0 -> 271 bytes .../textures/entity/spider/water.png | Bin 0 -> 881 bytes .../textures/entity/spider/water_eyes.png | Bin 0 -> 193 bytes .../textures/entity/spider/web_eyes.png | Bin 400 -> 380 bytes 43 files changed, 855 insertions(+), 76 deletions(-) create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/creeper/SandCreeperEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/creeper/SnowCreeperEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/enderman/FlameEndermanEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/enderman/RunicEndermanEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/silverfish/DesiccatedSilverfishEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/spider/FireSpiderEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/spider/WaterSpiderEntity.java create mode 100644 src/main/resources/assets/specialmobs/textures/entity/creeper/sand.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/creeper/snow.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/enderman/flame.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/enderman/flame_eyes.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/enderman/runic.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/enderman/runic_eyes.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/silverfish/desiccated.png rename src/main/resources/assets/specialmobs/textures/entity/silverfish/{fishy.png => puffer.png} (100%) create mode 100644 src/main/resources/assets/specialmobs/textures/entity/spider/fire.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/spider/fire_eyes.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/spider/water.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/spider/water_eyes.png diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java b/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java index 9daf3ed..08b575a 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java @@ -83,6 +83,11 @@ public class BestiaryInfo { EnvironmentEntry.builder( DefaultWeight.HIGH.value ).atNoMoonLight().build(), EnvironmentEntry.builder( DefaultWeight.LOW.value ).belowSeaLevel().build() ) ), + STORM( new EnvironmentList( + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).isThundering().build(), + EnvironmentEntry.builder( DefaultWeight.HIGH.value ).isRaining().build(), + EnvironmentEntry.builder( DefaultWeight.LOW.value ).cannotSeeSky().build() + ) ), FISHING( new EnvironmentList( EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).inWaterBiome().build(), EnvironmentEntry.builder( DefaultWeight.HIGH.value ).atMaxMoonLight().build(), @@ -487,6 +492,12 @@ public class BestiaryInfo { return this; } + /** Sets the species as NOT damaged by water. */ + public Builder waterInsensitive() { + isDamagedByWater = false; + return this; + } + /** Sets the species as leashable (can have a lead attached). */ public Builder leashable() { allowLeashing = true; diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java index 92f05c7..48ba7a8 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java @@ -40,18 +40,22 @@ public class MobFamily { public static final MobFamily CREEPER = new MobFamily<>( CreeperFamilyConfig::new, "Creeper", "creepers", 0x0DA70B, new EntityType[] { EntityType.CREEPER }, "Dark", "Death", "Dirt", "Doom", "Drowning", "Ender", "Fire", "Gravel", "Jumping", "Lightning", - "Mini", /*"Scope",*/ "Skeleton", "Splitting" - ); + "Mini", "Sand", /*"Scope",*/ "Snow", "Skeleton", "Splitting" + );//TODO scope public static final MobFamily ZOMBIE = new MobFamily<>( FamilyConfig::newLessSpecial, "Zombie", "zombies", 0x00AFAF, new EntityType[] { EntityType.ZOMBIE, EntityType.HUSK }, "Brute", "Fire", /*"Fishing",*/ "Giant", "Hungry", "Husk", "MadScientist", "Plague" - ); + );//TODO fishing // TODO Drowned family and zombie transform mechanic + // public static final MobFamily DROWNED = new MobFamily<>( FamilyConfig::new, + // "Zombie", "zombies", 0x8FF1D7, new EntityType[] { EntityType.DROWNED }, //VR egg: 0x799C65 + // "Brute", /*"Fishing",*/ "Giant", "Hungry", "Knight", "Plague" + // ); public static final MobFamily ZOMBIFIED_PIGLIN = new MobFamily<>( FamilyConfig::new, "ZombifiedPiglin", "zombified piglins", 0xEA9393, new EntityType[] { EntityType.ZOMBIFIED_PIGLIN }, "Brute", /*"Fishing",*/ "Giant", "Hungry", "Knight", "Plague", "Vampire"//TODO figure out crossbows - ); + );//TODO fishing public static final MobFamily SKELETON = new MobFamily<>( SkeletonFamilyConfig::new, "Skeleton", "skeletons", 0xC1C1C1, new EntityType[] { EntityType.SKELETON, EntityType.STRAY }, @@ -73,21 +77,21 @@ public class MobFamily { public static final MobFamily SPIDER = new MobFamily<>( FamilyConfig::newMoreSpecial, "Spider", "spiders", 0x342D27, new EntityType[] { EntityType.SPIDER }, - "Baby", "Desert", "Flying", "Giant", "Hungry", "Mother", "Pale", "Poison", /*"Water",*/ "Web", "Witch" + "Baby", "Desert", "Fire", "Flying", "Giant", "Hungry", "Mother", "Pale", "Poison", "Water", "Web", "Witch" ); public static final MobFamily CAVE_SPIDER = new MobFamily<>( FamilyConfig::newMoreSpecial, "CaveSpider", "cave spiders", 0x0C424E, new EntityType[] { EntityType.CAVE_SPIDER }, - "Baby", "Flying", "Mother", /*"Water",*/ "Web", "Witch" - ); + "Baby", /*"Desert", "Fire",*/ "Flying", "Mother", /*"Water",*/ "Web", "Witch" + );//TODO desert, fire, water public static final MobFamily SILVERFISH = new MobFamily<>( SilverfishFamilyConfig::new, "Silverfish", "silverfish", 0x6E6E6E, new EntityType[] { EntityType.SILVERFISH }, - "Blinding", /*"Fishing",*/ "Flying", "Poison", /*"Puffer",*/ "Tough" - ); + "Blinding", "Desiccated", /*"Fishing",*/ "Flying", "Poison", /*"Puffer",*/ "Tough" + );//TODO fishing, puffer public static final MobFamily ENDERMAN = new MobFamily<>( FamilyConfig::new, "Enderman", "endermen", 0x161616, new EntityType[] { EntityType.ENDERMAN }, - "Blinding", "Icy", "Lightning", "Mini", "Mirage", "Thief" + "Blinding", "Flame", "Icy", "Lightning", "Mini", "Mirage", "Runic", "Thief" ); public static final MobFamily WITCH = new MobFamily<>( WitchFamilyConfig::new, @@ -97,7 +101,7 @@ public class MobFamily { public static final MobFamily GHAST = new MobFamily<>( GhastFamilyConfig::new, "Ghast", "ghasts", 0xF9F9F9, new EntityType[] { EntityType.GHAST }, - "Baby", "Fighter", "King", "Queen", "Unholy", "CorporealShift" + "Baby", "CorporealShift", "Fighter", "King", "Queen", "Unholy" ); public static final MobFamily BLAZE = new MobFamily<>( FamilyConfig::new, diff --git a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java index 1c1a7d8..169c226 100644 --- a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java +++ b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java @@ -83,7 +83,7 @@ public class SpecialMobs { * + puffer * - endermen * - witches - * - ability to equip held items + * - ability to equip held items (wonky) * - uses splash speed instead of regular * - ghasts * - melee attack AI diff --git a/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java b/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java index a63aea2..489b7b6 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java @@ -103,14 +103,12 @@ public final class MobHelper { /** @return True if the damage source can deal normal damage to vampire-type mobs (e.g., wooden or smiting weapons). */ public static boolean isDamageSourceIneffectiveAgainstVampires( DamageSource source ) { - if( source != null ) { - if( source.isBypassMagic() || source.isBypassInvul() ) return false; - - final Entity attacker = source.getEntity(); - if( attacker instanceof LivingEntity ) { - final ItemStack weapon = ((LivingEntity) attacker).getMainHandItem(); - return !isWoodenTool( weapon ) && !hasSmite( weapon ); - } + if( source.isBypassMagic() || source.isBypassInvul() ) return false; + + final Entity attacker = source.getEntity(); + if( attacker instanceof LivingEntity ) { + final ItemStack weapon = ((LivingEntity) attacker).getMainHandItem(); + return !isWoodenTool( weapon ) && !hasSmite( weapon ); } return true; } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/blaze/ConflagrationBlazeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/blaze/ConflagrationBlazeEntity.java index 91678e8..d2e05fd 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/blaze/ConflagrationBlazeEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/blaze/ConflagrationBlazeEntity.java @@ -37,7 +37,7 @@ public class ConflagrationBlazeEntity extends _SpecialBlazeEntity { bestiaryInfo.color( 0xFFF87E ).weight( BestiaryInfo.DefaultWeight.LOW ) .uniqueTextureBaseOnly() .size( 1.5F, 0.9F, 2.7F ) - .addExperience( 4 ); + .addExperience( 4 ).regen( 20 ); } @SpecialMob.LanguageProvider @@ -106,6 +106,7 @@ public class ConflagrationBlazeEntity extends _SpecialBlazeEntity { getSpecialData().setRangedAttackMaxCooldown( getConfig().GENERAL.rangedAttackMaxCooldown.get() - cooldownReduction ); fireballBurstCount = getConfig().BLAZES.fireballBurstCount.get(); + if( growthLevel >= 3 ) fireballBurstCount++; if( growthLevel >= 7 ) fireballBurstCount++; final ModifiableAttributeInstance damage = getAttribute( Attributes.ATTACK_DAMAGE ); diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/DrowningCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/DrowningCreeperEntity.java index b49ea72..b472490 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/DrowningCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/DrowningCreeperEntity.java @@ -49,6 +49,9 @@ public class DrowningCreeperEntity extends _SpecialCreeperEntity { loot.addPool( new LootPoolBuilder( "common" ) .addEntry( new LootEntryItemBuilder( Items.COD ).setCount( 0, 2 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) .toLootPool() ); + loot.addPool( new LootPoolBuilder( "semicommon" ) + .addEntry( new LootEntryItemBuilder( Items.SALMON ).setCount( 0, 1 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) + .toLootPool() ); loot.addUncommonDrop( "uncommon", Items.GOLD_NUGGET, Items.PRISMARINE_SHARD, Items.PRISMARINE_CRYSTALS ); } @@ -66,6 +69,7 @@ public class DrowningCreeperEntity extends _SpecialCreeperEntity { public DrowningCreeperEntity( EntityType entityType, World world ) { super( entityType, world ); } /** Override to change this creeper's explosion power multiplier. */ + @Override protected float getVariantExplosionPower( float radius ) { return super.getVariantExplosionPower( radius ) + 3.0F; } /** Override to change this creeper's explosion. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/LightningCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/LightningCreeperEntity.java index d4ed789..0ee6a84 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/LightningCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/LightningCreeperEntity.java @@ -21,7 +21,7 @@ public class LightningCreeperEntity extends _SpecialCreeperEntity { @SpecialMob.BestiaryInfoSupplier public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { - bestiaryInfo.color( 0x499CAE ) + bestiaryInfo.color( 0x499CAE ).theme( BestiaryInfo.Theme.STORM ) .uniqueTextureBaseOnly() .addExperience( 1 ).fireImmune(); } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/SandCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/SandCreeperEntity.java new file mode 100644 index 0000000..ed6132e --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/SandCreeperEntity.java @@ -0,0 +1,153 @@ +package fathertoast.specialmobs.common.entity.creeper; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.util.ExplosionHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import net.minecraft.block.*; +import net.minecraft.block.material.Material; +import net.minecraft.entity.AreaEffectCloudEntity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.fluid.Fluids; +import net.minecraft.item.Items; +import net.minecraft.potion.EffectInstance; +import net.minecraft.potion.Effects; +import net.minecraft.tags.FluidTags; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.Explosion; +import net.minecraft.world.World; +import net.minecraft.world.storage.IServerWorldInfo; + +import java.util.List; + +@SpecialMob +public class SandCreeperEntity extends _SpecialCreeperEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { + bestiaryInfo.color( 0xE6DDAC ).theme( BestiaryInfo.Theme.DESERT ) + .uniqueTextureBaseOnly() + .addExperience( 1 ) + .addToAttribute( Attributes.ARMOR, 2.0 ) + .multiplyAttribute( Attributes.MOVEMENT_SPEED, 1.2 ); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Sand Creeper", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addClusterDrop( "common", Blocks.SAND ); + loot.addUncommonDrop( "uncommon", Blocks.CHISELED_SANDSTONE ); + loot.addRareDrop( "rare", Items.GOLD_INGOT ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return SandCreeperEntity::new; } + + /** @return This entity's mob species. */ + @SpecialMob.SpeciesSupplier + @Override + public MobFamily.Species getSpecies() { return SPECIES; } + + + //--------------- Variant-Specific Implementations ---------------- + + public SandCreeperEntity( EntityType entityType, World world ) { super( entityType, world ); } + + /** Override to change this creeper's explosion. */ + @Override + protected void makeVariantExplosion( float explosionPower ) { + final Explosion.Mode explosionMode = ExplosionHelper.getMode( this ); + + // Clear water before running the explosion so it can't shield blocks from damage + if( explosionMode != Explosion.Mode.NONE ) { + final int radius = (int) Math.floor( explosionPower ) + 2; + final BlockPos center = new BlockPos( position() ); + + for( int y = -radius; y <= radius; y++ ) { + for( int x = -radius; x <= radius; x++ ) { + for( int z = -radius; z <= radius; z++ ) { + if( x * x + y * y + z * z <= radius * radius ) { + clearWater( center.offset( x, y, z ) ); + } + } + } + } + } + + final ExplosionHelper explosion = new ExplosionHelper( this, explosionPower, explosionMode, false ); + if( !explosion.initializeExplosion() ) return; + explosion.finalizeExplosion(); + + // Clear weather + if( isPowered() && level.getLevelData() instanceof IServerWorldInfo ) { + final IServerWorldInfo serverInfo = (IServerWorldInfo) level.getLevelData(); + + serverInfo.setClearWeatherTime( random.nextInt( 12000 ) + 3600 ); + serverInfo.setRainTime( 0 ); + serverInfo.setRaining( false ); + serverInfo.setThunderTime( 0 ); + serverInfo.setThundering( false ); + } + } + + private void clearWater( BlockPos pos ) { + if( !level.getFluidState( pos ).is( FluidTags.WATER ) ) return; + + final BlockState block = level.getBlockState( pos ); + + if( block.getBlock() instanceof IBucketPickupHandler && + ((IBucketPickupHandler) block.getBlock()).takeLiquid( level, pos, block ) != Fluids.EMPTY ) { + // Removed through bucket pickup handler + return; + } + + if( block.getBlock() instanceof FlowingFluidBlock ) { + level.setBlock( pos, Blocks.AIR.defaultBlockState(), References.SET_BLOCK_FLAGS ); + return; + } + + final Material material = block.getMaterial(); + if( material == Material.WATER_PLANT || material == Material.REPLACEABLE_WATER_PLANT ) { + final TileEntity tileEntity = block.hasTileEntity() ? level.getBlockEntity( pos ) : null; + Block.dropResources( block, level, pos, tileEntity ); + level.setBlock( pos, Blocks.AIR.defaultBlockState(), References.SET_BLOCK_FLAGS ); + } + } + + /** + * Override to change effects applied by the lingering cloud left by this creeper's explosion. + * If this list is empty, the lingering cloud is not created. + */ + @Override + protected void modifyVariantLingeringCloudEffects( List potions ) { + potions.add( new EffectInstance( Effects.HUNGER, 600 ) ); + } + + /** Override to change stats of the lingering cloud left by this creeper's explosion. */ + @Override + protected void modifyVariantLingeringCloud( AreaEffectCloudEntity potionCloud ) { + final int duration = 40; + final float minRadius = 0.5F; + final float maxRadius = (getVariantExplosionPower( explosionRadius ) + 1.0F) * 3.0F; + + potionCloud.setDuration( duration ); + potionCloud.setRadius( minRadius ); + potionCloud.setRadiusPerTick( (maxRadius - minRadius) / (float) duration ); // Growing cloud + potionCloud.setRadiusOnUse( 0.0F ); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/SnowCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/SnowCreeperEntity.java new file mode 100644 index 0000000..5b286f2 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/SnowCreeperEntity.java @@ -0,0 +1,193 @@ +package fathertoast.specialmobs.common.entity.creeper; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.MobHelper; +import fathertoast.specialmobs.common.entity.ai.AIHelper; +import fathertoast.specialmobs.common.entity.ai.FluidPathNavigator; +import fathertoast.specialmobs.common.util.ExplosionHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.enchantment.FrostWalkerEnchantment; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.fluid.Fluid; +import net.minecraft.item.Items; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.pathfinding.PathNavigator; +import net.minecraft.pathfinding.PathNodeType; +import net.minecraft.potion.Effects; +import net.minecraft.state.properties.BlockStateProperties; +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.FluidTags; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.Explosion; +import net.minecraft.world.IServerWorld; +import net.minecraft.world.World; + +@SpecialMob +public class SnowCreeperEntity extends _SpecialCreeperEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { + bestiaryInfo.color( 0xE8F8F8 ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.ICE ) + .uniqueTextureBaseOnly() + .addExperience( 2 ).effectImmune( Effects.MOVEMENT_SLOWDOWN ) + .addToAttribute( Attributes.MAX_HEALTH, 10.0 ); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Snow Creeper", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addClusterDrop( "common", Items.SNOWBALL ); + loot.addUncommonDrop( "uncommon", Blocks.PACKED_ICE ); + loot.addRareDrop( "rare", Blocks.BLUE_ICE ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return SnowCreeperEntity::new; } + + /** @return This entity's mob species. */ + @SpecialMob.SpeciesSupplier + @Override + public MobFamily.Species getSpecies() { return SPECIES; } + + + //--------------- Variant-Specific Implementations ---------------- + + public SnowCreeperEntity( EntityType entityType, World world ) { + super( entityType, world ); + setPathfindingMalus( PathNodeType.WATER, PathNodeType.WALKABLE.getMalus() ); + } + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + AIHelper.replaceWaterAvoidingRandomWalking( this, 0.8 ); + } + + /** @return A new path navigator for this entity to use. */ + @Override + protected PathNavigator createNavigation( World world ) { + return new FluidPathNavigator( this, world, true, false ); + } + + /** @return Whether this entity can stand on a particular type of fluid. */ + @Override + public boolean canStandOnFluid( Fluid fluid ) { return fluid.is( FluidTags.WATER ); } + + /** Called each tick to update this entity. */ + @Override + public void tick() { + super.tick(); + MobHelper.floatInFluid( this, 0.06, FluidTags.WATER ); + } + + /** Called whenever this entity's block position changes. */ + @Override + protected void onChangedBlock( BlockPos pos ) { + super.onChangedBlock( pos ); + updateFrostWalker( pos ); + } + + /** Override to change this creeper's explosion power multiplier. */ + @Override + protected float getVariantExplosionPower( float radius ) { return super.getVariantExplosionPower( radius ) + 2.0F; } + + /** Override to change this creeper's explosion. */ + @Override + protected void makeVariantExplosion( float explosionPower ) { + final Explosion.Mode explosionMode = ExplosionHelper.getMode( this ); + final ExplosionHelper explosion = new ExplosionHelper( this, + explosionMode == Explosion.Mode.NONE ? explosionPower : 1.0F, explosionMode, false ); + if( !explosion.initializeExplosion() ) return; + explosion.finalizeExplosion(); + + if( explosionMode == Explosion.Mode.NONE ) return; + + final BlockState ice = Blocks.ICE.defaultBlockState(); + final int radius = (int) Math.floor( explosionPower ); + final int rMinusOneSq = (radius - 1) * (radius - 1); + final BlockPos center = new BlockPos( explosion.getPos() ); + //TODO make ice arena and then populate with some strays + + // for( int y = -radius; y <= radius; y++ ) { + // for( int x = -radius; x <= radius; x++ ) { + // for( int z = -radius; z <= radius; z++ ) { + // final int distSq = x * x + y * y + z * z; + // + // if( distSq <= radius * radius ) { + // final BlockPos pos = center.offset( x, y, z ); + // final BlockState stateAtPos = level.getBlockState( pos ); + // + // if( stateAtPos.getMaterial().isReplaceable() || stateAtPos.is( BlockTags.LEAVES ) ) { + // if( distSq > rMinusOneSq ) { + // // "Coral" casing + // level.setBlock( pos, random.nextFloat() < 0.25F ? brainCoral : hornCoral, References.SET_BLOCK_FLAGS ); + // } + // else { + // final float fillChoice = random.nextFloat(); + // + // if( fillChoice < 0.1F && seaPickle.canSurvive( level, pos ) ) { + // level.setBlock( pos, seaPickle, References.SET_BLOCK_FLAGS ); + // } + // else if( fillChoice < 0.3F && seaGrass.canSurvive( level, pos ) ) { + // level.setBlock( pos, seaGrass, References.SET_BLOCK_FLAGS ); + // } + // else { + // // Water fill + // level.setBlock( pos, water, References.SET_BLOCK_FLAGS ); + // + // // Prevent greater radiuses from spawning a bazillion pufferfish + // if( random.nextFloat() < 0.01F && pufferCount < 10 ) { + // spawnStray( pos ); + // pufferCount++; + // } + // } + // } + // } + // } + // } + // } + // } + } + + /** Helper method to simplify spawning strays. */ + private void spawnStray( BlockPos pos ) { + //TODO + // if( !(level instanceof IServerWorld) ) return; + // final PufferfishEntity lePuffPuff = EntityType.PUFFERFISH.create( level ); + // if( lePuffPuff != null ) { + // lePuffPuff.setPos( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); + // level.addFreshEntity( lePuffPuff ); + // } + } + + /** Override to load data from this entity's NBT data. */ + @Override + public void readVariantSaveData( CompoundNBT saveTag ) { + setPathfindingMalus( PathNodeType.WATER, PathNodeType.WALKABLE.getMalus() ); + } + + /** Called to make the frost walker ice platform around this entity, as needed. */ + private void updateFrostWalker( BlockPos pos ) { + final boolean actualOnGround = onGround; + onGround = true; // Spoof the frost walker enchant requirement to be on the ground + FrostWalkerEnchantment.onEntityMoved( this, level, pos, 1 ); + onGround = actualOnGround; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/enderman/FlameEndermanEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/enderman/FlameEndermanEntity.java new file mode 100644 index 0000000..7bf671b --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/enderman/FlameEndermanEntity.java @@ -0,0 +1,63 @@ +package fathertoast.specialmobs.common.entity.enderman; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.item.Items; +import net.minecraft.world.World; + +@SpecialMob +public class FlameEndermanEntity extends _SpecialEndermanEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { + bestiaryInfo.color( 0xDC1A00 ).theme( BestiaryInfo.Theme.FIRE ) + .uniqueTextureWithEyes() + .addExperience( 2 ).fireImmune() + .addToAttribute( Attributes.MAX_HEALTH, 10.0 ); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Flame Enderman", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.FIRE_CHARGE ); + loot.addUncommonDrop( "uncommon", Items.COAL ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return FlameEndermanEntity::new; } + + /** @return This entity's mob species. */ + @SpecialMob.SpeciesSupplier + @Override + public MobFamily.Species getSpecies() { return SPECIES; } + + + //--------------- Variant-Specific Implementations ---------------- + + public FlameEndermanEntity( EntityType entityType, World world ) { super( entityType, world ); } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + target.setSecondsOnFire( 5 ); + } + + // TODO implement ability to create ring of fire around target on teleport +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/enderman/IcyEndermanEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/enderman/IcyEndermanEntity.java index f3de53b..f584865 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/enderman/IcyEndermanEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/enderman/IcyEndermanEntity.java @@ -40,7 +40,7 @@ public class IcyEndermanEntity extends _SpecialEndermanEntity { @SpecialMob.BestiaryInfoSupplier public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { - bestiaryInfo.color( 0x72959C ).theme( BestiaryInfo.Theme.ICE ) + bestiaryInfo.color( 0xDDEAEA ).theme( BestiaryInfo.Theme.ICE ) .uniqueTextureWithEyes() .addExperience( 1 ).effectImmune( Effects.MOVEMENT_SLOWDOWN ); @@ -56,7 +56,7 @@ public class IcyEndermanEntity extends _SpecialEndermanEntity { public static void buildLootTable( LootTableBuilder loot ) { addBaseLoot( loot ); loot.addClusterDrop( "common", Items.SNOWBALL ); - loot.addUncommonDrop( "uncommon", Blocks.ICE ); + loot.addUncommonDrop( "uncommon", Blocks.BLUE_ICE ); } @SpecialMob.Factory @@ -88,6 +88,7 @@ public class IcyEndermanEntity extends _SpecialEndermanEntity { } /** Override to change this entity's AI goals. */ + @Override protected void registerVariantGoals() { AIHelper.replaceWaterAvoidingRandomWalking( this, 1.0 ); } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/enderman/LightningEndermanEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/enderman/LightningEndermanEntity.java index 4f58059..8f4492f 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/enderman/LightningEndermanEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/enderman/LightningEndermanEntity.java @@ -24,9 +24,9 @@ public class LightningEndermanEntity extends _SpecialEndermanEntity { @SpecialMob.BestiaryInfoSupplier public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { - bestiaryInfo.color( 0x4BB4B5 ) + bestiaryInfo.color( 0x4BB4B5 ).theme( BestiaryInfo.Theme.STORM ) .uniqueTextureWithEyes() - .addExperience( 2 ).fireImmune(); + .addExperience( 2 ).fireImmune().waterInsensitive(); } @SpecialMob.LanguageProvider diff --git a/src/main/java/fathertoast/specialmobs/common/entity/enderman/MirageEndermanEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/enderman/MirageEndermanEntity.java index 1b5472a..52e12dc 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/enderman/MirageEndermanEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/enderman/MirageEndermanEntity.java @@ -9,6 +9,7 @@ import net.minecraft.block.Blocks; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.item.Items; import net.minecraft.nbt.CompoundNBT; import net.minecraft.util.DamageSource; import net.minecraft.world.World; @@ -41,6 +42,7 @@ public class MirageEndermanEntity extends _SpecialEndermanEntity { loot.addGuaranteedDrop( "base", Blocks.SAND, 1 ); loot.addUncommonDrop( "uncommon", Blocks.INFESTED_STONE, Blocks.INFESTED_COBBLESTONE, Blocks.INFESTED_STONE_BRICKS, Blocks.INFESTED_CRACKED_STONE_BRICKS, Blocks.INFESTED_MOSSY_STONE_BRICKS, Blocks.INFESTED_CHISELED_STONE_BRICKS ); + loot.addRareDrop( "rare", Items.GOLD_INGOT ); } @SpecialMob.Factory diff --git a/src/main/java/fathertoast/specialmobs/common/entity/enderman/RunicEndermanEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/enderman/RunicEndermanEntity.java new file mode 100644 index 0000000..26d99db --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/enderman/RunicEndermanEntity.java @@ -0,0 +1,71 @@ +package fathertoast.specialmobs.common.entity.enderman; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.MobHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.potion.EffectInstance; +import net.minecraft.potion.Effects; +import net.minecraft.world.World; + +@SpecialMob +public class RunicEndermanEntity extends _SpecialEndermanEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { + bestiaryInfo.color( 0xE42281 ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.MOUNTAIN ) + .uniqueTextureWithEyes() + .addExperience( 2 ).fallImmune().burnImmune() + .addToAttribute( Attributes.ARMOR, 10.0 ) + .addToAttribute( Attributes.ATTACK_DAMAGE, 1.0 ) + .multiplyAttribute( Attributes.MOVEMENT_SPEED, 0.8 ); + + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Runic Enderman", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + //TODO add drops + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return RunicEndermanEntity::new; } + + /** @return This entity's mob species. */ + @SpecialMob.SpeciesSupplier + @Override + public MobFamily.Species getSpecies() { return SPECIES; } + + + //--------------- Variant-Specific Implementations ---------------- + + public RunicEndermanEntity( EntityType entityType, World world ) { super( entityType, world ); } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( target instanceof LivingEntity ) { + final LivingEntity livingTarget = (LivingEntity) target; + final int duration = MobHelper.getDebuffDuration( level.getDifficulty() ); + + livingTarget.addEffect( new EffectInstance( Effects.LEVITATION, duration ) ); + } + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/ghast/CorporealShiftGhastEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/ghast/CorporealShiftGhastEntity.java index 8cc1360..97cc3a0 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/ghast/CorporealShiftGhastEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/ghast/CorporealShiftGhastEntity.java @@ -11,7 +11,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.ai.attributes.Attributes; -import net.minecraft.entity.projectile.FireballEntity; import net.minecraft.nbt.CompoundNBT; import net.minecraft.network.datasync.DataParameter; import net.minecraft.network.datasync.DataSerializers; @@ -65,7 +64,7 @@ public class CorporealShiftGhastEntity extends _SpecialGhastEntity { public static final DataParameter CORPOREAL = EntityDataManager.defineId( CorporealShiftGhastEntity.class, DataSerializers.BOOLEAN ); - private final int maxShiftTime = 600; + private final int maxShiftTime = 150; private int shiftTime = maxShiftTime; public CorporealShiftGhastEntity( EntityType entityType, World world ) { super( entityType, world ); } @@ -74,6 +73,7 @@ public class CorporealShiftGhastEntity extends _SpecialGhastEntity { protected void defineSynchedData() { super.defineSynchedData(); entityData.define( CORPOREAL, false ); + if( !level.isClientSide() && random.nextBoolean() ) setCorporeal( true ); } @Override @@ -82,7 +82,7 @@ public class CorporealShiftGhastEntity extends _SpecialGhastEntity { if( --shiftTime <= 0 ) { if( !level.isClientSide ) { - shiftTime = maxShiftTime; + shiftTime = maxShiftTime + random.nextInt( maxShiftTime ); setCorporeal( !isCorporeal() ); spawnShiftSmoke( (ServerWorld) level ); } @@ -90,31 +90,32 @@ public class CorporealShiftGhastEntity extends _SpecialGhastEntity { } private void spawnShiftSmoke( ServerWorld world ) { - world.sendParticles( ParticleTypes.CLOUD, this.getX(), this.getY(), this.getZ(), 25, 0.0, 0.0, 0.0, 0.4 ); + world.sendParticles( ParticleTypes.CLOUD, this.getX(), this.getY(), this.getZ(), + 25, 0.0, 0.0, 0.0, 0.4 ); } - + @Override - public boolean isPushable() { - return isCorporeal() && super.isPushable(); - } - + public boolean isPushable() { return isCorporeal() && super.isPushable(); } + @Override - protected void doPush(Entity entity) { - if (isCorporeal()) - super.doPush(entity); - } - + protected void doPush( Entity entity ) { if( isCorporeal() ) super.doPush( entity ); } + + /** @return Attempts to damage this entity; returns true if the hit was successful. */ + @Override + public boolean hurt( DamageSource source, float amount ) { return isCorporeal() && super.hurt( source, amount ); } + public boolean isCorporeal() { return entityData.get( CORPOREAL ); } private void setCorporeal( boolean value ) { entityData.set( CORPOREAL, value ); } - /** Override to change this ghast's explosion power multiplier. */ - @Override - protected int getVariantExplosionPower( int radius ) { return Math.round( radius * 2.5F ); } - /** Called to attack the target with a ranged attack. */ @Override public void performRangedAttack( LivingEntity target, float damageMulti ) { + if( isCorporeal() ) { + super.performRangedAttack( target, damageMulti ); + return; + } + if( !isSilent() ) level.levelEvent( null, References.EVENT_GHAST_SHOOT, blockPosition(), 0 ); final float accelVariance = MathHelper.sqrt( distanceTo( target ) ) * 0.5F * getSpecialData().getRangedAttackSpread(); @@ -122,34 +123,14 @@ public class CorporealShiftGhastEntity extends _SpecialGhastEntity { double dX = target.getX() - (getX() + lookVec.x) + getRandom().nextGaussian() * accelVariance; double dY = target.getY( 0.5 ) - (0.5 + getY( 0.5 )); double dZ = target.getZ() - (getZ() + lookVec.z) + getRandom().nextGaussian() * accelVariance; - - if (isCorporeal()) { - FireballEntity fireball = new FireballEntity( level, this, dX, dY, dZ ); - fireball.explosionPower = getVariantExplosionPower( getExplosionPower() ); - fireball.setPos( - getX() + lookVec.x, - getY( 0.5 ) + 0.5, - getZ() + lookVec.z ); - level.addFreshEntity( fireball ); - } - else { - IncorporealFireballEntity fireball = new IncorporealFireballEntity( level, this, dX, dY, dZ ); - fireball.explosionPower = getVariantExplosionPower( getExplosionPower() ); - fireball.setPos( - getX() + lookVec.x, - getY( 0.5 ) + 0.5, - getZ() + lookVec.z ); - level.addFreshEntity( fireball ); - } - } - - - //--------------- SpecialMobData Hooks ---------------- - - /** @return Attempts to damage this entity; returns true if the hit was successful. */ - @Override - public boolean hurt( DamageSource source, float amount ) { - return isCorporeal() && super.hurt( source, amount ); + + final IncorporealFireballEntity fireball = new IncorporealFireballEntity( level, this, dX, dY, dZ ); + fireball.explosionPower = getVariantExplosionPower( getExplosionPower() ); + fireball.setPos( + getX() + lookVec.x, + getY( 0.5 ) + 0.5, + getZ() + lookVec.z ); + level.addFreshEntity( fireball ); } /** Override to save data to this entity's NBT data. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/DesiccatedSilverfishEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/DesiccatedSilverfishEntity.java new file mode 100644 index 0000000..3238615 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/DesiccatedSilverfishEntity.java @@ -0,0 +1,113 @@ +package fathertoast.specialmobs.common.entity.silverfish; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.MobHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import net.minecraft.block.*; +import net.minecraft.block.material.Material; +import net.minecraft.entity.CreatureAttribute; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.fluid.Fluids; +import net.minecraft.potion.EffectInstance; +import net.minecraft.potion.Effects; +import net.minecraft.tags.FluidTags; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +@SpecialMob +public class DesiccatedSilverfishEntity extends _SpecialSilverfishEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { + bestiaryInfo.color( 0xE7E7E7 ).theme( BestiaryInfo.Theme.DESERT ) + .uniqueTextureBaseOnly() + .addExperience( 1 ).undead() + .addToAttribute( Attributes.MAX_HEALTH, 4.0 ).addToAttribute( Attributes.ARMOR, 2.0 ); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Desiccated Silverfish", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addLootTable( "common", EntityType.ZOMBIE.getDefaultLootTable() ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return DesiccatedSilverfishEntity::new; } + + /** @return This entity's mob species. */ + @SpecialMob.SpeciesSupplier + @Override + public MobFamily.Species getSpecies() { return SPECIES; } + + + //--------------- Variant-Specific Implementations ---------------- + + public DesiccatedSilverfishEntity( EntityType entityType, World world ) { super( entityType, world ); } + + /** Called each tick to update this entity's movement. */ + @Override + public void aiStep() { + clearWater( new BlockPos( position() ) ); + super.aiStep(); + } + + private void clearWater( BlockPos pos ) { + if( !level.getFluidState( pos ).is( FluidTags.WATER ) ) return; + + final BlockState block = level.getBlockState( pos ); + + if( block.getBlock() instanceof IBucketPickupHandler && + ((IBucketPickupHandler) block.getBlock()).takeLiquid( level, pos, block ) != Fluids.EMPTY ) { + // Removed through bucket pickup handler + heal( 1.0F ); + return; + } + + if( block.getBlock() instanceof FlowingFluidBlock ) { + level.setBlock( pos, Blocks.AIR.defaultBlockState(), References.SET_BLOCK_FLAGS ); + heal( 1.0F ); + return; + } + + final Material material = block.getMaterial(); + if( material == Material.WATER_PLANT || material == Material.REPLACEABLE_WATER_PLANT ) { + final TileEntity tileEntity = block.hasTileEntity() ? level.getBlockEntity( pos ) : null; + Block.dropResources( block, level, pos, tileEntity ); + level.setBlock( pos, Blocks.AIR.defaultBlockState(), References.SET_BLOCK_FLAGS ); + heal( 1.0F ); + } + } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( target instanceof LivingEntity ) { + final LivingEntity livingTarget = (LivingEntity) target; + final int duration = MobHelper.getDebuffDuration( level.getDifficulty() ); + + livingTarget.addEffect( new EffectInstance( Effects.HUNGER, duration ) ); + } + } + + /** @return This entity's creature type. */ + @Override + public CreatureAttribute getMobType() { return CreatureAttribute.UNDEAD; } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/FishingSilverfishEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/FishingSilverfishEntity.java index 09ab124..899c1aa 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/FishingSilverfishEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/FishingSilverfishEntity.java @@ -51,6 +51,9 @@ public class FishingSilverfishEntity extends _SpecialSilverfishEntity implements loot.addPool( new LootPoolBuilder( "common" ) .addEntry( new LootEntryItemBuilder( Items.COD ).setCount( 0, 2 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) .toLootPool() ); + loot.addPool( new LootPoolBuilder( "semicommon" ) + .addEntry( new LootEntryItemBuilder( Items.SALMON ).setCount( 0, 1 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) + .toLootPool() ); } @SpecialMob.Factory diff --git a/src/main/java/fathertoast/specialmobs/common/entity/slime/BlueberrySlimeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/slime/BlueberrySlimeEntity.java index 9ec96eb..182b004 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/slime/BlueberrySlimeEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/slime/BlueberrySlimeEntity.java @@ -66,6 +66,7 @@ public class BlueberrySlimeEntity extends _SpecialSlimeEntity { } /** Override to change this entity's AI goals. */ + @Override protected void registerVariantGoals() { AIHelper.removeGoals( goalSelector, 1 ); // SlimeEntity.FloatGoal } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/slime/LemonSlimeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/slime/LemonSlimeEntity.java index e2f7121..2048ebe 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/slime/LemonSlimeEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/slime/LemonSlimeEntity.java @@ -29,7 +29,7 @@ public class LemonSlimeEntity extends _SpecialSlimeEntity { @SpecialMob.BestiaryInfoSupplier public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { - bestiaryInfo.color( 0xE6E861 ) + bestiaryInfo.color( 0xE6E861 ).theme( BestiaryInfo.Theme.STORM ) .uniqueTextureBaseOnly() .addExperience( 2 ).fireImmune() .addToAttribute( Attributes.MAX_HEALTH, 4.0 ); diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/FireSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/FireSpiderEntity.java new file mode 100644 index 0000000..9d974d6 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/FireSpiderEntity.java @@ -0,0 +1,68 @@ +package fathertoast.specialmobs.common.entity.spider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import net.minecraft.block.Blocks; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.Items; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +@SpecialMob +public class FireSpiderEntity extends _SpecialSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { + bestiaryInfo.color( 0xDFA21B ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.FIRE ) + .uniqueTextureWithEyes() + .addExperience( 2 ).fireImmune().waterSensitive(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Firefang", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.FIRE_CHARGE ); + loot.addUncommonDrop( "uncommon", Items.COAL ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return FireSpiderEntity::new; } + + /** @return This entity's mob species. */ + @SpecialMob.SpeciesSupplier + @Override + public MobFamily.Species getSpecies() { return SPECIES; } + + + //--------------- Variant-Specific Implementations ---------------- + + public FireSpiderEntity( EntityType entityType, World world ) { super( entityType, world ); } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( !level.isClientSide() && target instanceof LivingEntity ) { + final BlockPos pos = target.blockPosition(); + if( level.getBlockState( pos ).getMaterial().isReplaceable() ) { + level.setBlock( pos, Blocks.FIRE.defaultBlockState(), References.SET_BLOCK_FLAGS ); + } + } + target.setSecondsOnFire( 5 ); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/HungrySpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/HungrySpiderEntity.java index f4b8f38..66e0992 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/spider/HungrySpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/HungrySpiderEntity.java @@ -8,6 +8,7 @@ import fathertoast.specialmobs.common.util.References; import fathertoast.specialmobs.datagen.loot.LootTableBuilder; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.entity.ai.attributes.Attributes; import net.minecraft.entity.ai.attributes.ModifiableAttributeInstance; @@ -81,7 +82,9 @@ public class HungrySpiderEntity extends _SpecialSpiderEntity { /** Override to apply effects when this entity hits a target with a melee attack. */ @Override protected void onVariantAttack( Entity target ) { - if( !level.isClientSide() && target instanceof PlayerEntity && ForgeEventFactory.getMobGriefingEvent( level, this ) ) { + if( level.isClientSide() ) return; + + if( target instanceof PlayerEntity && ForgeEventFactory.getMobGriefingEvent( level, this ) ) { final ItemStack food = MobHelper.stealRandomFood( (PlayerEntity) target ); if( !food.isEmpty() ) { final float previousHealth = getMaxHealth(); @@ -95,6 +98,9 @@ public class HungrySpiderEntity extends _SpecialSpiderEntity { playSound( SoundEvents.PLAYER_BURP, 0.5F, random.nextFloat() * 0.1F + 0.9F ); } } + else if( target instanceof LivingEntity ) { + MobHelper.stealLife( this, (LivingEntity) target, 2.0F ); + } } /** Recalculates the modifiers associated with this entity's feeding level counters. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/WaterSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/WaterSpiderEntity.java new file mode 100644 index 0000000..1d87062 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/WaterSpiderEntity.java @@ -0,0 +1,100 @@ +package fathertoast.specialmobs.common.entity.spider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.MobHelper; +import fathertoast.specialmobs.common.entity.ai.AIHelper; +import fathertoast.specialmobs.common.entity.ai.FluidPathNavigator; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootEntryItemBuilder; +import fathertoast.specialmobs.datagen.loot.LootPoolBuilder; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.fluid.Fluid; +import net.minecraft.item.Items; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.pathfinding.PathNavigator; +import net.minecraft.pathfinding.PathNodeType; +import net.minecraft.tags.FluidTags; +import net.minecraft.world.World; + +@SpecialMob +public class WaterSpiderEntity extends _SpecialSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { + bestiaryInfo.color( 0x2D41F4 ).theme( BestiaryInfo.Theme.WATER ) + .uniqueTextureWithEyes() + .addExperience( 1 ).drownImmune().fluidPushImmune() + .addToAttribute( Attributes.ATTACK_DAMAGE, 1.0 ); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Water Spider", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addPool( new LootPoolBuilder( "common" ) + .addEntry( new LootEntryItemBuilder( Items.COD ).setCount( 0, 2 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) + .toLootPool() ); + loot.addPool( new LootPoolBuilder( "semicommon" ) + .addEntry( new LootEntryItemBuilder( Items.SALMON ).setCount( 0, 1 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) + .toLootPool() ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return WaterSpiderEntity::new; } + + /** @return This entity's mob species. */ + @SpecialMob.SpeciesSupplier + @Override + public MobFamily.Species getSpecies() { return SPECIES; } + + + //--------------- Variant-Specific Implementations ---------------- + + public WaterSpiderEntity( EntityType entityType, World world ) { + super( entityType, world ); + setPathfindingMalus( PathNodeType.WATER, PathNodeType.WALKABLE.getMalus() ); + } + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + AIHelper.replaceWaterAvoidingRandomWalking( this, 0.8 ); + } + + /** @return A new path navigator for this entity to use. */ + @Override + protected PathNavigator createNavigation( World world ) { + return new FluidPathNavigator( this, world, true, false ); + } + + /** @return Whether this entity can stand on a particular type of fluid. */ + @Override + public boolean canStandOnFluid( Fluid fluid ) { return fluid.is( FluidTags.WATER ); } + + /** Called each tick to update this entity. */ + @Override + public void tick() { + super.tick(); + MobHelper.floatInFluid( this, 0.06, FluidTags.WATER ); + } + + /** Override to load data from this entity's NBT data. */ + @Override + public void readVariantSaveData( CompoundNBT saveTag ) { + setPathfindingMalus( PathNodeType.WATER, PathNodeType.WALKABLE.getMalus() ); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombie/FishingZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/FishingZombieEntity.java index 523e0d0..fee425e 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/zombie/FishingZombieEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombie/FishingZombieEntity.java @@ -63,6 +63,9 @@ public class FishingZombieEntity extends _SpecialZombieEntity implements IAngler loot.addPool( new LootPoolBuilder( "common" ) .addEntry( new LootEntryItemBuilder( Items.COD ).setCount( 0, 2 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) .toLootPool() ); + loot.addPool( new LootPoolBuilder( "semicommon" ) + .addEntry( new LootEntryItemBuilder( Items.SALMON ).setCount( 0, 1 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) + .toLootPool() ); loot.addPool( new LootPoolBuilder( "rare" ).addConditions( LootHelper.RARE_CONDITIONS ) .addEntry( new LootEntryItemBuilder( Items.FISHING_ROD ).enchant( 30, true ).toLootEntry() ) .toLootPool() ); diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombie/MadScientistZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/MadScientistZombieEntity.java index 9f2e7fd..594b094 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/zombie/MadScientistZombieEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombie/MadScientistZombieEntity.java @@ -36,7 +36,7 @@ public class MadScientistZombieEntity extends _SpecialZombieEntity implements IA @SpecialMob.BestiaryInfoSupplier public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { - bestiaryInfo.color( 0xDED4C6 ).weight( BestiaryInfo.DefaultWeight.LOW ) + bestiaryInfo.color( 0xDED4C6 ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.STORM ) .uniqueTextureBaseOnly() .addExperience( 2 ).disableRangedAttack(); } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/FishingZombifiedPiglinEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/FishingZombifiedPiglinEntity.java index 00150e6..d7ec4d5 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/FishingZombifiedPiglinEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/FishingZombifiedPiglinEntity.java @@ -61,7 +61,10 @@ public class FishingZombifiedPiglinEntity extends _SpecialZombifiedPiglinEntity public static void buildLootTable( LootTableBuilder loot ) { addBaseLoot( loot ); loot.addPool( new LootPoolBuilder( "common" ) - .addEntry( new LootEntryItemBuilder( Items.SALMON ).setCount( 0, 2 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) + .addEntry( new LootEntryItemBuilder( Items.COD ).setCount( 0, 2 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) + .toLootPool() ); + loot.addPool( new LootPoolBuilder( "semicommon" ) + .addEntry( new LootEntryItemBuilder( Items.SALMON ).setCount( 0, 1 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) .toLootPool() ); loot.addPool( new LootPoolBuilder( "rare" ).addConditions( LootHelper.RARE_CONDITIONS ) .addEntry( new LootEntryItemBuilder( Items.FISHING_ROD ).enchant( 30, true ).toLootEntry() ) diff --git a/src/main/resources/assets/specialmobs/textures/entity/cave_spider/web_eyes.png b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/web_eyes.png index eebc22bbf404d84a6a0a4246a4f0c6aa444c455c..d4561a2aad2ca2747439b469626413de12d2c3a6 100644 GIT binary patch delta 344 zcmV-e0jK`@1N;Jz8DRqe005Okji^hme_;c6fDfZfl2AV1`m(9}yJ*006_0r;h*t010$bPE-H?|NsC0 z|Nj6}Pk0iMAs2rD)k#D_R7gwhlhF>tAP7Z4+{*ud@h;RR6S1+`vh1Pfp<0!z;Fa`4 zZh>3C_?-|00004T07;T0Ns=T<$|X%vF@Mc9NhFujDDz(6qwQFj zbZsBIjP5=&p^)~EafWUZw*R`AXK_mygd-9^j(3vpF#p?Kq)NbSI!POECz_(7b9sXR z>z!o#-FgG}eGjD0ob6a>=q7O~MKhK>fA*7(v!p31D&;D&t*AKP;!=v8cI#6k*b6Y8 zq`XTtb-cjG+pBBRX}9pCb{d^$?}qz_?%}57PvA#5J{z+74E`GbuEUb_hw*j2D4Sv& zVvWIzN@3fva5-PB)%Btnx=C!u!fl-rvBuyPTuPCZwPZpfY+&dnDOO6r65&#cv*{#p zq*?n!6j%h{qp)Gytb=UA$MXUoAID<=cw1M9T4N+OMA*&05Fc&+`RC6imEJ-ck_RjS hLOY#jhxign?^e<{?Lg47qjdlP002ovPDHLkV1i8e>B#^9 diff --git a/src/main/resources/assets/specialmobs/textures/entity/creeper/sand.png b/src/main/resources/assets/specialmobs/textures/entity/creeper/sand.png new file mode 100644 index 0000000000000000000000000000000000000000..474601b18df86e40fc0e675b6594e1c30c765ae2 GIT binary patch literal 626 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/specialmobs/textures/entity/creeper/splitting.png b/src/main/resources/assets/specialmobs/textures/entity/creeper/splitting.png index f40b191c828892b9e3567b4203c606c6b497cee2..4cf73a150405171fc19ff7ff91170d7182b72733 100644 GIT binary patch delta 5921 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 Hu0mjfcAY7+ delta 75 zcmeAb`z*R)1DCU&r;B4qhV$E#2RRuGc$f{}?=RuvY}K0(uKF&qfPvvb@zvh)_}lgj dKyYB+69zpW{*Rya7X1S8Jzf1=);T3K0RTD3947z( diff --git a/src/main/resources/assets/specialmobs/textures/entity/enderman/flame.png b/src/main/resources/assets/specialmobs/textures/entity/enderman/flame.png new file mode 100644 index 0000000000000000000000000000000000000000..8cfb4a4b981ce5702d5532347c1517fb1dba3776 GIT binary patch literal 576 zcmV-G0>AxPx#1ZP1_K>z@;j|==^1poj57*I@9MLjVuB^@6}JUL@iN@7z=eq~ty|Nj600EKOH z1poj52y{|TQ~&?}|NsC00N#VWSpWb432;bRa{vGi!vFvd!vV){sAK>D0i8)iK~zXf zrBnfugdhw96#f5?FG&M}%-ync8HAQ5q3W*bHj?3V0~vtM*`0FA82>X!Ul>qMj0POr z20Cyukbb{|Fc<^~r~DIi;LG5vhbS@O{Oi{UYIGnJWqc&qD98xi)31h&q8ZAPOTk=} zsK5x5`3r&(`Xb~5fCa&di{>v-kO>BU! ztYR{MfeZqC0w=mh3mNyp4y28u`3s^9fUFk0f2#qS?su@hDsQuqzh5KayaQ(oP6bmt zradtRSU(%6m-b}h)iM&q7$7dlP6cjocQo+U`a|K<<8UgF39egc2He^(5Rqcar_dYb zb+n?>b`nQJ%VIUJLk9p(d9Q2~L#;*dj4cBRo6dn|uVzkQ1%Hf-bTPXwfNNo4F*HfFz%2alCF(Q$oU8L)g6K;abp+& O0000Nn{1``2&1HTu8eR=R!5~i;#}E(ix0fyXfY!PMPTTo^=Fyh0m=4~Dp*L!JIa`+Sl_abZ zQ4n+ZVHNR`Kdy8^RfFq|;|Ht~ZkKL~j%QXA4lsJvzV!i@sU6$5{5u|O4U7&=^3#_; Sp8OPOI)kUHpUXO@geCwseoQw2 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/specialmobs/textures/entity/enderman/runic.png b/src/main/resources/assets/specialmobs/textures/entity/enderman/runic.png new file mode 100644 index 0000000000000000000000000000000000000000..9a6c3274a23c5386d56430d15e719de358b97afa GIT binary patch literal 632 zcmV-;0*C#HP)Px#1ZP1_K>z@;j|==^1poj55>QN3MSp*PkB^UNXlVcc|8#V80002zgU^-#000Jb zQchF<|NsC00JFcOzW@LL32;bRa{vGi!vFvd!vV){sAK>D0o_SNK~zXfrIZPF#2^eq z!!q~3a`k1AN1ApfeN{H>Si*qecm+pm?GR!;3g$^afEpf*#|tzxhK z&EEwfdDa!>$y2*`Tw}^1zQ!|E8HvBaQ!c<~1zCi8@oD9k*t-nWr!rvbiJeo~=&gr* z7z=W$wVy%nz+tQNyd*1urUAVXpm)GvXHrnw6obq;YG&RD)JzNFddA{k6*;?xqI<@D zL+FC|@tw$@$(qJ>Lt^e^ssY3_5_;m|bHs-5uL7zt6`o%Hn&T1#j@aN>1;;dS;N1n{ zx^EuJnLDOIq+*l862SF9z=4pVUz%|w?!@{T(*i15x@P0B^Vl{t_0pZV6YfBW$yli= zQB)LF-;~LsKN3)ZN`S%=L;ZWl7Ms!DBmjTvt4%HzMM)ZJSB-1CB8`d(H!Qyg&Zc6Q zdJ?L_2i!*jQ^$Nn{1`IRkt`T#pIXJW*-{@-~FMaRXA!B|(0{|Nk>wKgA1F%fMOS5n0T@z;_6Q z8AUa`8i0bAJzX3_JiOmdxh-@^fybrs|No3~^Oz?783FNg3e&f2JS*qaq5F2B*n%s! z7Ecjdu**V`f5)WTOy2@NyXN%GRx)#_`)hrE*?NWxw#OZnxn>_c&&bbzJ8nXd&UAsg zhe^tfyK6o;8tqZ-tLodl(%GGtLBZ+Kngf@eL^W2g_dKbRl(8;{Th1UVS>&#j=7wXD z^#{%~7;M@Vv4=lm1JlyA2jc>n8m3m;xKHh3JY&VsQ^a8T)qm-S)qD}lnDRH4PY_`? usE=CmlAGxR*PHg^!Dq}rE@*GK7PqN}U+a$iN^YR{7(8A5T-G@yGywpNDT2QM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/specialmobs/textures/entity/silverfish/desiccated.png b/src/main/resources/assets/specialmobs/textures/entity/silverfish/desiccated.png new file mode 100644 index 0000000000000000000000000000000000000000..752616bedebed8d6f08242c77a10ce1b1f4d4a6d GIT binary patch literal 577 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3-p)I`?e@QjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`Z328kTr20K7EX=MY!9;tQv3MvTC+*~P znvgnCw_|;}6pwiHJ0?cai;EpPChhAnKCHQfTl=-r8Lh(D`LBQFma)g@%t&wYU9I=c z!QsJPhnB53gkp}Bd~nq{vgnkBT;~eA>T@~y3hWy44(t*)e^sVUd^mabj0S1OssjBD zKc74-zr-u^V~3-$$g=>=&6_TBv_5FwD%2*RGryd*=EJ?HlgUDBB+VKhKjAsG@V#f* z7oW58$2Rk6MF)RDm4>}*Jc2GE4 z#%<)k^^@bW8w^7F;VY{hyQFRZ8B9?t&sgZBDoqN%?k6%kWaau+@Eh4y`e3pG; z(YKFO!vG^t0D);!7BB&pd|`fNPjOpCs*!M)ePPGJu}3&6e?DbUuBnl6T1A_DVE_OC z!bubd0000WbW%=J|NsC0|NsC0|NsC0|NsC0|NsC0|NsC0|NsC0|Ns90TwELNks%j< z0@z7JK~zXfm6hvasxT0Qb5RJPV5Oy6TI&11<@sh4t+e*XSx8tmJF}NWbQcrnywl+F zmrIB{yJKM5+cQS)>wCczsRZ6P-r%r65cUSZJ4%gu?s(-XwP1=|2^ujd3+H#mOOXu- z4PY6Si>U8&xFe_xe!mNbvXm_oQ5KGWQe6B^;D8qb_27}Pf{%0fwQbwnysiYL2fF_v z04RonZ@&rPRB-D*t9N^`6@ZE1J?LgbKxSnJKvhk8UB3xvaH(}I6hy4%Zvurzvs|Rt z^_wXFl-gEJkXJQ};KqPr#h6~#Z$en%lX>A$&FTRlGtoa$8^P}$q)zZI3BcHY5Y%JH z!`6yk*Kgtg7krUwzHVcYocq{wXPw%ooS!&Lj%_1%6(9m_2B|dc9Z``DL7#{5FpNX& zV+aZpLiDX~QsbYG>DYD0^n6ss38NL#G#i2##=~J4#sEIM$-A6`b{*&AOP4zC<4f5# zYnIe)24NaP7$*H_=mUNb$Tz)zay=hOLrS4gc=fJgP5OBn-`>Wl??d*@sX3i;lozpI z6;e#fIX*xGPY(8I)WJ3@*0E3-^_%xkiaQpuxfMFbUil}`;&f5>@BiWXq1E9f0 z0?7T>@3FXfUe}pvo-xAH zGWb$pLDsNZ0#<~DWzk}DD@&qO%to9=#hVCt3aaayfxy(G`SF%=*346h^UqT+`wuBS zuyqUa2f==?ehH3PGQuM~rK1EDC2PfO#OWVQ9pW|yLom!FAVv~m{6(NxCMB%RTO-ht eRK?uMZV?->qS)wanp-w+C@90ZDO0WqLyh4RV%Z`MnghkJH}4%H-E1`Xu}%s!a2w1b?gwc zWPi!>`@YY4pY!~m=Q+Poq;FVKHs{q}dJ`#6R^0~NI{{hav_n`sc#L=;;%VuUK zmc-+7(0C1yD^|$l3UzsW36I0RM!u&v0KiNvDX$;){`;4%8d`B`24gEhcc+i;P9I*k zU0yeX>KxTTjDJQ+2+)yp+00U&D^_%Jd2KY-b*Xw4dE+~3+l|7sp3v*Ilg?`LUv)ww z@$s+qRaK)h1+8wh(I|upEi<5*GX$DugV897KB6Y9!Bhb{r>!78V zUO9ENIyN#0pvwXQkpYc0V6&mtA`ygiR+IgYjSNcr+@5-nvKftZR(q&j>t=vTSnX7x zRbAL@2=aoUs7u1GX5`iT^BOvKfs?G^umO z@X#*VPh+-j)DaIYf{jGfVyIrXYX84NE0uI(zrQbl-D*WEm1G-1R{S?+@OqFAP(7$d zxD}dZ$yU~cbXMcw{$T(~-fp!@nd+pCl84oGfPXq>Rb?s~{shgJJ1b*W>JN;H6ncT_Wy-Ez{N{fNG@gw_&wOIRvr0^m#*mAKFaWG zyF!2Oxg%8o*G>!hLJ6m%mWJXwcdv}0TGcIrX5&i?_V@v~y|Bpc)s-8-V>`AnHZmxu zEq{^LTB#)Ga*_H1!U3OtHmxg);ytL%>w8^iqjW(23hDxGkD`2hnx9!w?!CX)f5+fF)P=Kk^nc64l~XIB?J zJzl17-N67j9X2A-Bn$C0hyU{{e)Gxa9Dg`G!TERIuQ0H@u8$z-70=fiHbP%M|(zHK`@Ivyimtnl@bK7Y*S z4w;IDLQxK{KmUD!XwJZQzcb0u-UHZHGiqq>0RnyxKfUlLqB#S0r<-j?ldN5ZLXm}d zdQF|`?(|VCm+9{GVKH|A5BV5s2B*VHu2>3l58Glj|3-~?!{4zvy2KgWGdnEZI<4+Rsdw-ZbJ|Xp} zz|Gt5ejuN@c3SFz_fWA6Fm?J2uf048Kyop|8*lwWUaM1Je}912&s|{Zt)CI_*U76hf|$7g z!Z&a0W~oQYfWCkir`;`8Y^)6;jjc729KDkY=e zxRgyUW|%sC27pL3iB>A%7(B@9=Pt;#!0YEO;21oJRw@x$&Ah48XQWeP-*;0jSF0j3 z+s=S|p@hw{gBMRe1HjwwegM>J0KnC2H)>Pjs)L+Z=y=?SZ*`hWq<>O;`Naye@g=#D zp*kWGt+ngzcFU-JvugmTO>ttnTU?6r(Ut46Z7h)`5=}N;3p8%#s7`5n9q`ie@oG=E zm+`}+%tw>V&c^}RJrHD|FTmff{fk(lW`vjt_U{b=;Pcq2t{U{&jX3~*`?3Rx zZD&BPSb;>E;zys7FMpKy_Nik4{QASo%+B2*Unt2%P`04)?9==3dF-Sz8mWwi&toTX zH$|qP;c;y2n|&+sNJUW2@eZr3{!WKgk{8$=>>`oUr(`Olv2SFM<@;Yf(kA9+qiqMM zyRZ2|i9ddPT|RGYcp|(UV@!2*@tkX2!@)Gt+H=3U=$Jf-6#K8BMeW+5E-(NNRuy=2?f1Ut`ZmIRYx?+ z!qYLG{`lRT)SW9<)xx`6G@jVxiWNdZ zuN?1CuK@x=*IYbJDClJ_o^E}Tw23f`j}BA;4vmI%1*?&`3iTjUg@}?k!V*v&(Re~6 z+KQttfi)*dt9q~AnBUwYC_2v<%DRFiijUJ_;m~MEw`|Q6G-*_>SYal<#7ul?jZ^jn zyErr&l0&*eL9ecWiZedYJ6SzfRGqXHkQpEhZiU7yXlA&0u0Cr=C)Bb_T_rY>-|P(d YFV8paiZlXFsQ>@~07*qoM6N<$f{XT6D*ylh diff --git a/src/main/resources/assets/specialmobs/textures/entity/spider/desert_eyes.png b/src/main/resources/assets/specialmobs/textures/entity/spider/desert_eyes.png index bcaa2f07fb396fbedb2697af0e36fefc21e78ebf..b608ae35648dd05e2d582606db4a554fe43cbf6b 100644 GIT binary patch delta 160 zcmdnNc#&~}iWFmUkh>GZx^prw85kH?(j9#r85lP9bN@+Xov0{Lo#yG{7?R=q_Tom) zh5(+HhtN1w-+~Z9x&i>xpw#X`d$?SD3ye>h`{~%SD=N;@^Heu)Rj>>+QY!^Y*gU>atay oT9#Y5G-p@d|FiEv8W?z%G1z=nuz8wgkq6>>y85}Sb4q9e01n+ZwEzGB diff --git a/src/main/resources/assets/specialmobs/textures/entity/spider/fire.png b/src/main/resources/assets/specialmobs/textures/entity/spider/fire.png new file mode 100644 index 0000000000000000000000000000000000000000..d6631d68441c9487deae573ef496bf1be5acd111 GIT binary patch literal 841 zcmV-P1GfB$P)Px#1ZP1_K>z@;j|==^1poj5Cs0gOMKvcWK{GW^JU*9AEy-*G-=Z7u%~mueCs{;C zVoXkTSz0F=8vpkt8mI0@M2qM1!YoOGTB8~ci=GysMvr;|j3a8}k>@xAkC!|MuvRFP z#;q&>{wAnVZssApc$D5kN!|#Op_UsNP=Zrd#1JKcd2nTc3{YuFg#rPn0f%0Ex9YHVh)iqz&=-;Czz6#rN#qEaPR}35+LQ*N`)1-#L4m zXFt2{-Utym&`vNV>VtxVcd5$%nsN2k+Z9@y)kfSBYgq(JBMWr+s8=JdE8y1JwYsx1 z+zGVNG>E2&K$+rJ>ug7ie$H|B5l5R4D7sxfG8l}@N<#E}?G``B(fWz>b{s!Tw|4ns z7mS8O1S^R@1NA980fV3_vNn{1``2&1HT&Mf$p3GsozexK1*)X7(pmAP4kYX(f@(cbC1Ps5o@dX0~I14-? ziy0WWg+Z8+Vb&Z8pkRfki(`m~_uI+QTn!2WuFtprH!Yv46Ms-b#HG0InBaRy*OSW< zW(A8H&;6>N&spR+dHL)!!A$mwKV~klS=`qut1w4ZH?HyB%^?0gOo=HI-Et!>nm$Zm zvttZDS;aGlNk_QmMG13>dxDD&SIbIo3)br`3M`%HxzCvCJ(KsebDAX!bOVE@tDnm{ Hr-UW|Jseg@ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/specialmobs/textures/entity/spider/water.png b/src/main/resources/assets/specialmobs/textures/entity/spider/water.png new file mode 100644 index 0000000000000000000000000000000000000000..d94d1d0561b91900124053c61ce92da717052891 GIT binary patch literal 881 zcmV-%1CIQOP)Px#1ZP1_K>z@;j|==^1poj5Gf+%aMcmM|<=VkTJTEXQ8*N}sh;>|4M>JDPHCaqL zXD0;)+wK~zXfb(M?4svr=A1+utU_WnmTiKLRmCDa6P9W|m#HCz9+MJ-GEM zFmj*>2;qNVCKG1LF_%*EX>|w?KeuxDO8}x2I=_4b9CrWdalBn*AXe@eVdqJJHG#Ni zPyI1}q7ebqmI@LiYz#|)2?g&U?WFr-{saQCRs>+nY7E5a=1+F=0r53Ixak)5#EdUXc8qf9qm_Lzft6bt5WBAy{j;0hV%L>@YIcY@Q zO#)8`%t&(FU=moO{6t_OlWjR&3*|$?6>xc1!N-ITDNC?ZLdvj?GjoZFTX-yr&%)ggN>n#C}vf%R@DJUL>~fu5eH|&;uWw~AO#77 z_d`KvS&3Cq4$cIJ>W`HQTUPo}KuDTyt#PIYX9A^#q3PN;S`1wRX7;_XPdo=_!lEPN zzY4xGHdd8#dq_Nuj?c^BOi*!SCG=(eoCi^m#^*cu!TbFW=$=Tu(Y^~B00000NkvXX Hu0mjfPp*EF literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/specialmobs/textures/entity/spider/water_eyes.png b/src/main/resources/assets/specialmobs/textures/entity/spider/water_eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..75f5d412a4baa3e6b6b34f4304d5636e374b8983 GIT binary patch literal 193 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3HE3&8=$zQjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`ISV`@iy0XB4uLSEsD@VqP%zBX#W5tq`RzqRt_A}hhl_o3?jJ+;+V60i zAjaM0*L3asvI{=;zCeZfrcY#2@0@-7ac#EmhnJ33Cc1**8_R#6zy0Xc`Rg~#cHJ#s jf9#)@6j0^7375DEPO3+*xBQ(7=K_vn0qb_&*dd1p8MAO;i-GXSnL= z;uzxL{dV$2zQYO}u8wz3{jdM1cSTKjsuI|UB!T{}OJWnV$MXP*9{4>RBAtb0)TM4x5G0ZER}JReT}^<2&= obKuch^9^Oam$RcpXDs@~RLg6-P0FI@5zyldp00i_>zopr0A=WvOaK4? delta 384 zcmV-`0e}Ad0+0ic8Gi-<005$$euMx3010qNS#tmY3ljhU3ljkVnw%H_00BZtL_t(& zf$f)1O2se`#wSO3xG2JdETy=Y?)3zE2tA2j>b(oRT6b1l3W1V^XZYNhIK))2HhSqA9(;6haR!HpMUV3>}IcoFaUgx2V+gR z$l|4c*zd9gK(`&PZtiB+yS%HuOZAZ zuZFeHf$I5pf_Vku4-@wFshG2o0SgiF<;z$j+03;P% eGdcc5!aHw59VRA%bIkJq0000 Date: Sat, 23 Jul 2022 21:37:22 -0500 Subject: [PATCH 15/16] le fix --- .../DesiccatedSilverfishSpeciesConfig.java | 35 +++ .../specialmobs/common/entity/MobHelper.java | 50 ++++- .../cavespider/WebCaveSpiderEntity.java | 2 +- .../entity/creeper/DirtCreeperEntity.java | 2 +- .../entity/creeper/DrowningCreeperEntity.java | 8 +- .../creeper/LightningCreeperEntity.java | 4 +- .../entity/creeper/SandCreeperEntity.java | 4 +- .../entity/creeper/SnowCreeperEntity.java | 201 ++++++++++++------ .../entity/creeper/_SpecialCreeperEntity.java | 2 +- .../entity/enderman/FlameEndermanEntity.java | 77 ++++++- .../entity/enderman/RunicEndermanEntity.java | 7 +- .../DesiccatedSilverfishEntity.java | 116 +++++++--- .../entity/spider/FireSpiderEntity.java | 2 +- .../common/entity/spider/WebSpiderEntity.java | 2 +- .../specialmobs/common/util/References.java | 45 ++-- .../textures/entity/creeper/sand.png | Bin 626 -> 635 bytes .../textures/entity/creeper/snow_eyes.png | Bin 0 -> 147 bytes .../textures/entity/enderman/flame.png | Bin 576 -> 581 bytes .../textures/entity/enderman/flame_eyes.png | Bin 233 -> 223 bytes .../textures/entity/enderman/runic.png | Bin 632 -> 661 bytes .../textures/entity/enderman/runic_eyes.png | Bin 354 -> 351 bytes .../textures/entity/silverfish/desiccated.png | Bin 577 -> 738 bytes .../textures/entity/spider/fire.png | Bin 841 -> 830 bytes .../textures/entity/spider/fire_eyes.png | Bin 271 -> 272 bytes .../textures/entity/spider/water.png | Bin 881 -> 873 bytes .../textures/entity/spider/web_eyes.png | Bin 380 -> 380 bytes 26 files changed, 434 insertions(+), 123 deletions(-) create mode 100644 src/main/java/fathertoast/specialmobs/common/config/species/DesiccatedSilverfishSpeciesConfig.java create mode 100644 src/main/resources/assets/specialmobs/textures/entity/creeper/snow_eyes.png diff --git a/src/main/java/fathertoast/specialmobs/common/config/species/DesiccatedSilverfishSpeciesConfig.java b/src/main/java/fathertoast/specialmobs/common/config/species/DesiccatedSilverfishSpeciesConfig.java new file mode 100644 index 0000000..0fe038c --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/species/DesiccatedSilverfishSpeciesConfig.java @@ -0,0 +1,35 @@ +package fathertoast.specialmobs.common.config.species; + +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.config.Config; +import fathertoast.specialmobs.common.config.field.IntField; +import fathertoast.specialmobs.common.config.file.ToastConfigSpec; +import fathertoast.specialmobs.common.config.util.ConfigUtil; + +public class DesiccatedSilverfishSpeciesConfig extends SilverfishSpeciesConfig { + + public final Desiccated DESICCATED; + + /** Builds the config spec that should be used for this config. */ + public DesiccatedSilverfishSpeciesConfig( MobFamily.Species species, double spitChance, int minAbsorb, int maxAbsorb ) { + super( species, spitChance ); + + DESICCATED = new Desiccated( SPEC, species, speciesName, minAbsorb, maxAbsorb ); + } + + public static class Desiccated extends Config.AbstractCategory { + + public final IntField.RandomRange absorbCount; + + Desiccated( ToastConfigSpec parent, MobFamily.Species species, String speciesName, int minAbsorb, int maxAbsorb ) { + super( parent, ConfigUtil.camelCaseToLowerUnderscore( species.specialVariantName ), + "Options specific to " + speciesName + "." ); + + absorbCount = new IntField.RandomRange( + SPEC.define( new IntField( "water_absorbed.min", minAbsorb, IntField.Range.NON_NEGATIVE, + "The minimum and maximum (inclusive) number of water blocks " + speciesName + " can absorb." ) ), + SPEC.define( new IntField( "water_absorbed.max", maxAbsorb, IntField.Range.NON_NEGATIVE ) ) + ); + } + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java b/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java index 489b7b6..9ca02b4 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java @@ -2,8 +2,14 @@ package fathertoast.specialmobs.common.entity; import fathertoast.specialmobs.common.config.Config; import fathertoast.specialmobs.common.entity.creeper._SpecialCreeperEntity; +import fathertoast.specialmobs.common.util.References; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; import net.minecraft.block.FlowingFluidBlock; +import net.minecraft.block.material.Material; import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.enchantment.FrostWalkerEnchantment; import net.minecraft.entity.*; import net.minecraft.entity.monster.CreeperEntity; import net.minecraft.entity.player.PlayerEntity; @@ -14,11 +20,14 @@ import net.minecraft.item.*; import net.minecraft.potion.EffectInstance; import net.minecraft.potion.EffectType; import net.minecraft.potion.Effects; +import net.minecraft.tags.FluidTags; import net.minecraft.tags.ITag; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.DamageSource; import net.minecraft.util.Hand; import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundEvents; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.util.math.vector.Vector3d; import net.minecraft.world.Difficulty; @@ -325,7 +334,7 @@ public final class MobHelper { * Floats the entity upward if they are in a given fluid type. Used by entities that walk on fluids so their * AI doesn't break if they wind up inside that fluid. *

- * Should be called in the entity's #tick() loop right after super. + * Should be called in the entity's {@link Entity#tick()} loop right after super. * * @param entity The entity to float. * @param acceleration How fast the entity floats upward. @@ -342,4 +351,43 @@ public final class MobHelper { } } } + + /** + * Manually provides the frost walker enchantment's effects without any equipment requirements. + *

+ * Should be called in the entity's {@link LivingEntity#onChangedBlock(BlockPos)} method right after super. + * + * @param entity The entity. + * @param pos The block pos argument from #onChangedBlock. + */ + public static void updateFrostWalker( LivingEntity entity, BlockPos pos ) { + final boolean actualOnGround = entity.isOnGround(); + entity.setOnGround( true ); // Spoof the frost walker enchant requirement to be on the ground + FrostWalkerEnchantment.onEntityMoved( entity, entity.level, pos, 1 ); + entity.setOnGround( actualOnGround ); + } + + /** + * Pops the entity upward if they are on top of the water, so that their frost walker ability can take effect. + *

+ * Should be called in the entity's {@link Entity#tick()} loop after both super and the call to floatInFluid. + * + * @param entity The entity to pop. + */ + public static void hopOnFluid( Entity entity ) { + if( entity.tickCount > 1 && entity.level.random.nextInt( 20 ) == 0 ) { + if( ISelectionContext.of( entity ).isAbove( FlowingFluidBlock.STABLE_SHAPE, entity.blockPosition(), true ) && + !entity.level.getFluidState( entity.blockPosition().above() ).is( FluidTags.WATER ) ) { + // Break water plants, otherwise frost walker will not work + final BlockState block = entity.level.getBlockState( entity.blockPosition() ); + if( block.getMaterial() == Material.WATER_PLANT || block.getMaterial() == Material.REPLACEABLE_WATER_PLANT ) { + final TileEntity tileEntity = block.hasTileEntity() ? entity.level.getBlockEntity( entity.blockPosition() ) : null; + Block.dropResources( block, entity.level, entity.blockPosition(), tileEntity ); + entity.level.setBlock( entity.blockPosition(), Blocks.WATER.defaultBlockState(), References.SetBlockFlags.DEFAULTS ); + } + + entity.setDeltaMovement( entity.getDeltaMovement().scale( 0.5 ).add( 0.0, 0.4, 0.0 ) ); + } + } + } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/WebCaveSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/WebCaveSpiderEntity.java index 4ab19f0..56b3a5d 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/WebCaveSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/WebCaveSpiderEntity.java @@ -97,7 +97,7 @@ public class WebCaveSpiderEntity extends _SpecialCaveSpiderEntity { /** @return Attempts to place a cobweb at the given position and returns true if successful. */ private boolean tryPlaceWeb( BlockPos pos ) { if( level.getBlockState( pos ).getMaterial().isReplaceable() ) { - level.setBlock( pos, Blocks.COBWEB.defaultBlockState(), References.SET_BLOCK_FLAGS ); + level.setBlock( pos, Blocks.COBWEB.defaultBlockState(), References.SetBlockFlags.DEFAULTS ); webCount--; return true; } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/DirtCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/DirtCreeperEntity.java index f25bc8b..0f3d291 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/DirtCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/DirtCreeperEntity.java @@ -78,7 +78,7 @@ public class DirtCreeperEntity extends _SpecialCreeperEntity { if( x * x + y * y + z * z <= radius * radius ) { final BlockPos pos = center.offset( x, y, z ); if( level.getBlockState( pos ).getMaterial().isReplaceable() ) { - level.setBlock( pos, dirt, References.SET_BLOCK_FLAGS ); + level.setBlock( pos, dirt, References.SetBlockFlags.DEFAULTS ); } } } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/DrowningCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/DrowningCreeperEntity.java index b472490..1494442 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/DrowningCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/DrowningCreeperEntity.java @@ -108,20 +108,20 @@ public class DrowningCreeperEntity extends _SpecialCreeperEntity { if( stateAtPos.getMaterial().isReplaceable() || stateAtPos.is( BlockTags.LEAVES ) ) { if( distSq > rMinusOneSq ) { // "Coral" casing - level.setBlock( pos, random.nextFloat() < 0.25F ? brainCoral : hornCoral, References.SET_BLOCK_FLAGS ); + level.setBlock( pos, random.nextFloat() < 0.25F ? brainCoral : hornCoral, References.SetBlockFlags.DEFAULTS ); } else { final float fillChoice = random.nextFloat(); if( fillChoice < 0.1F && seaPickle.canSurvive( level, pos ) ) { - level.setBlock( pos, seaPickle, References.SET_BLOCK_FLAGS ); + level.setBlock( pos, seaPickle, References.SetBlockFlags.DEFAULTS ); } else if( fillChoice < 0.3F && seaGrass.canSurvive( level, pos ) ) { - level.setBlock( pos, seaGrass, References.SET_BLOCK_FLAGS ); + level.setBlock( pos, seaGrass, References.SetBlockFlags.DEFAULTS ); } else { // Water fill - level.setBlock( pos, water, References.SET_BLOCK_FLAGS ); + level.setBlock( pos, water, References.SetBlockFlags.DEFAULTS ); // Prevent greater radiuses from spawning a bazillion pufferfish if( random.nextFloat() < 0.01F && pufferCount < 10 ) { diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/LightningCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/LightningCreeperEntity.java index 0ee6a84..753bb95 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/LightningCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/LightningCreeperEntity.java @@ -53,7 +53,9 @@ public class LightningCreeperEntity extends _SpecialCreeperEntity { /** Override to change this creeper's explosion power multiplier. */ @Override - protected float getVariantExplosionPower( float radius ) { return radius * (isPowered() ? 2.0F : 1.0F / 3.0F); } + protected float getVariantExplosionPower( float radius ) { + return isSupercharged() || isPowered() ? super.getVariantExplosionPower( radius ) : radius / 3.0F; + } /** Override to change this creeper's explosion. */ @Override diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/SandCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/SandCreeperEntity.java index ed6132e..5ab9281 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/SandCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/SandCreeperEntity.java @@ -117,7 +117,7 @@ public class SandCreeperEntity extends _SpecialCreeperEntity { } if( block.getBlock() instanceof FlowingFluidBlock ) { - level.setBlock( pos, Blocks.AIR.defaultBlockState(), References.SET_BLOCK_FLAGS ); + level.setBlock( pos, Blocks.AIR.defaultBlockState(), References.SetBlockFlags.DEFAULTS ); return; } @@ -125,7 +125,7 @@ public class SandCreeperEntity extends _SpecialCreeperEntity { if( material == Material.WATER_PLANT || material == Material.REPLACEABLE_WATER_PLANT ) { final TileEntity tileEntity = block.hasTileEntity() ? level.getBlockEntity( pos ) : null; Block.dropResources( block, level, pos, tileEntity ); - level.setBlock( pos, Blocks.AIR.defaultBlockState(), References.SET_BLOCK_FLAGS ); + level.setBlock( pos, Blocks.AIR.defaultBlockState(), References.SetBlockFlags.DEFAULTS ); } } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/SnowCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/SnowCreeperEntity.java index 5b286f2..276ad37 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/SnowCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/SnowCreeperEntity.java @@ -6,13 +6,14 @@ import fathertoast.specialmobs.common.bestiary.SpecialMob; import fathertoast.specialmobs.common.entity.MobHelper; import fathertoast.specialmobs.common.entity.ai.AIHelper; import fathertoast.specialmobs.common.entity.ai.FluidPathNavigator; +import fathertoast.specialmobs.common.entity.skeleton.StraySkeletonEntity; import fathertoast.specialmobs.common.util.ExplosionHelper; import fathertoast.specialmobs.common.util.References; import fathertoast.specialmobs.datagen.loot.LootTableBuilder; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; -import net.minecraft.enchantment.FrostWalkerEnchantment; import net.minecraft.entity.EntityType; +import net.minecraft.entity.SpawnReason; import net.minecraft.entity.ai.attributes.Attributes; import net.minecraft.fluid.Fluid; import net.minecraft.item.Items; @@ -20,10 +21,10 @@ import net.minecraft.nbt.CompoundNBT; import net.minecraft.pathfinding.PathNavigator; import net.minecraft.pathfinding.PathNodeType; import net.minecraft.potion.Effects; -import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.tags.BlockTags; import net.minecraft.tags.FluidTags; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; import net.minecraft.world.Explosion; import net.minecraft.world.IServerWorld; import net.minecraft.world.World; @@ -39,7 +40,7 @@ public class SnowCreeperEntity extends _SpecialCreeperEntity { @SpecialMob.BestiaryInfoSupplier public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { bestiaryInfo.color( 0xE8F8F8 ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.ICE ) - .uniqueTextureBaseOnly() + .uniqueTextureWithEyes() .addExperience( 2 ).effectImmune( Effects.MOVEMENT_SLOWDOWN ) .addToAttribute( Attributes.MAX_HEALTH, 10.0 ); } @@ -95,86 +96,160 @@ public class SnowCreeperEntity extends _SpecialCreeperEntity { public void tick() { super.tick(); MobHelper.floatInFluid( this, 0.06, FluidTags.WATER ); + MobHelper.hopOnFluid( this ); } /** Called whenever this entity's block position changes. */ @Override protected void onChangedBlock( BlockPos pos ) { super.onChangedBlock( pos ); - updateFrostWalker( pos ); + MobHelper.updateFrostWalker( this, pos ); } /** Override to change this creeper's explosion power multiplier. */ @Override - protected float getVariantExplosionPower( float radius ) { return super.getVariantExplosionPower( radius ) + 2.0F; } + protected float getVariantExplosionPower( float radius ) { return super.getVariantExplosionPower( radius ) + 3.0F; } /** Override to change this creeper's explosion. */ @Override protected void makeVariantExplosion( float explosionPower ) { final Explosion.Mode explosionMode = ExplosionHelper.getMode( this ); final ExplosionHelper explosion = new ExplosionHelper( this, - explosionMode == Explosion.Mode.NONE ? explosionPower : 1.0F, explosionMode, false ); + explosionMode == Explosion.Mode.NONE ? explosionPower : 2.0F, false, false ); if( !explosion.initializeExplosion() ) return; explosion.finalizeExplosion(); - if( explosionMode == Explosion.Mode.NONE ) return; - - final BlockState ice = Blocks.ICE.defaultBlockState(); final int radius = (int) Math.floor( explosionPower ); - final int rMinusOneSq = (radius - 1) * (radius - 1); final BlockPos center = new BlockPos( explosion.getPos() ); - //TODO make ice arena and then populate with some strays - // for( int y = -radius; y <= radius; y++ ) { - // for( int x = -radius; x <= radius; x++ ) { - // for( int z = -radius; z <= radius; z++ ) { - // final int distSq = x * x + y * y + z * z; - // - // if( distSq <= radius * radius ) { - // final BlockPos pos = center.offset( x, y, z ); - // final BlockState stateAtPos = level.getBlockState( pos ); - // - // if( stateAtPos.getMaterial().isReplaceable() || stateAtPos.is( BlockTags.LEAVES ) ) { - // if( distSq > rMinusOneSq ) { - // // "Coral" casing - // level.setBlock( pos, random.nextFloat() < 0.25F ? brainCoral : hornCoral, References.SET_BLOCK_FLAGS ); - // } - // else { - // final float fillChoice = random.nextFloat(); - // - // if( fillChoice < 0.1F && seaPickle.canSurvive( level, pos ) ) { - // level.setBlock( pos, seaPickle, References.SET_BLOCK_FLAGS ); - // } - // else if( fillChoice < 0.3F && seaGrass.canSurvive( level, pos ) ) { - // level.setBlock( pos, seaGrass, References.SET_BLOCK_FLAGS ); - // } - // else { - // // Water fill - // level.setBlock( pos, water, References.SET_BLOCK_FLAGS ); - // - // // Prevent greater radiuses from spawning a bazillion pufferfish - // if( random.nextFloat() < 0.01F && pufferCount < 10 ) { - // spawnStray( pos ); - // pufferCount++; - // } - // } - // } - // } - // } - // } - // } - // } + if( explosionMode != Explosion.Mode.NONE ) { + final BlockState ice = Blocks.ICE.defaultBlockState(); + final int rMinusOneSq = (radius - 1) * (radius - 1); + + for( int y = -radius; y <= radius; y++ ) { + for( int x = -radius; x <= radius; x++ ) { + for( int z = -radius; z <= radius; z++ ) { + final int distSq = x * x + y * y + z * z; + + if( distSq <= radius * radius ) { + final BlockPos pos = center.offset( x, y, z ); + + // Freeze top layer of water and temporary ice within affected volume + final BlockState block = level.getBlockState( pos ); + if( block.is( Blocks.FROSTED_ICE ) || block.getFluidState().is( FluidTags.WATER ) ) { + final BlockState blockAbove = level.getBlockState( pos.above() ); + if( !blockAbove.getMaterial().blocksMotion() && !blockAbove.getFluidState().is( FluidTags.WATER ) ) + level.setBlock( pos, ice, References.SetBlockFlags.DEFAULTS ); + } + + // Attempt to place pillars along circumference only + if( y == 0 && distSq > rMinusOneSq ) placePillar( pos, radius ); + } + } + } + } + } + + final int strays = radius / 2; + for( int count = 0; count < strays; count++ ) { + for( int attempt = 0; attempt < 8; attempt++ ) { + if( trySpawnStray( center, radius ) ) break; + } + } } - /** Helper method to simplify spawning strays. */ - private void spawnStray( BlockPos pos ) { - //TODO - // if( !(level instanceof IServerWorld) ) return; - // final PufferfishEntity lePuffPuff = EntityType.PUFFERFISH.create( level ); - // if( lePuffPuff != null ) { - // lePuffPuff.setPos( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); - // level.addFreshEntity( lePuffPuff ); - // } + /** Try to place an ice pillar at the location. */ + private void placePillar( BlockPos pos, int radius ) { + final BlockPos.Mutable currentPos = pos.mutable(); + if( shouldReplace( currentPos ) ) findGroundBelow( currentPos, radius ); + else if( findGroundAbove( currentPos, radius ) ) return; + + final BlockState ice = Blocks.PACKED_ICE.defaultBlockState(); + final int maxY = Math.min( currentPos.getY() + 4, level.getMaxBuildHeight() - 2 ); + int height = -2; // This is minimum pillar height + if( pos.getY() > currentPos.getY() ) height -= (pos.getY() - currentPos.getY()) / 2; + + while( currentPos.getY() < maxY && shouldReplace( currentPos ) ) { + level.setBlock( currentPos, ice, References.SetBlockFlags.DEFAULTS ); + currentPos.move( 0, 1, 0 ); + + if( ++height >= 0 && random.nextBoolean() ) break; + } + } + + /** Attempts to find the ground. Resets the position if none can be found. */ + private void findGroundBelow( BlockPos.Mutable currentPos, int radius ) { + final int yI = currentPos.getY(); + final int minY = Math.max( yI - radius, 0 ); + + while( currentPos.getY() > minY ) { + currentPos.move( 0, -1, 0 ); + if( !shouldReplace( currentPos ) ) { + // Move back up one to ensure the current pos is replaceable + currentPos.move( 0, 1, 0 ); + return; + } + } + // Initial y was replaceable, so we can default to this + currentPos.setY( yI ); + } + + /** @return Attempts to find the ground. Returns true if the pillar should be canceled. */ + private boolean findGroundAbove( BlockPos.Mutable currentPos, int radius ) { + final int yI = currentPos.getY(); + final int maxY = Math.min( yI + radius, level.getMaxBuildHeight() - 2 ); + + while( currentPos.getY() < maxY ) { + currentPos.move( 0, 1, 0 ); + // Found a replaceable pos + if( shouldReplace( currentPos ) ) return false; + } + // Initial y was not replaceable, so we must cancel the entire operation + return true; + } + + /** @return True if a generating pillar should replace the block at a particular position. */ + private boolean shouldReplace( BlockPos pos ) { + final BlockState stateAtPos = level.getBlockState( pos ); + return (stateAtPos.getMaterial().isReplaceable() || stateAtPos.is( BlockTags.LEAVES )) && + !stateAtPos.getFluidState().is( FluidTags.WATER ); + } + + /** @return Helper method to simplify spawning strays. Returns true if it spawns one. */ + private boolean trySpawnStray( BlockPos center, int radius ) { + if( !(level instanceof IServerWorld) ) return false; + final StraySkeletonEntity stray = StraySkeletonEntity.SPECIES.entityType.get().create( level ); + if( stray == null ) return false; + + // Pick a random position within the ice prison, then cancel if we can't spawn at that position + final float angle = random.nextFloat() * 2.0F * (float) Math.PI; + final float distance = random.nextFloat() * (radius - 1); + final BlockPos.Mutable currentPos = center.mutable().move( + MathHelper.floor( MathHelper.cos( angle ) * distance ), + 0, + MathHelper.floor( MathHelper.sin( angle ) * distance ) + ); + if( shouldReplace( currentPos ) ) findGroundBelow( currentPos, radius ); + else if( findGroundAbove( currentPos, radius ) ) { + stray.remove(); + return false; // No floor found + } + stray.moveTo( currentPos, angle * 180.0F / (float) Math.PI + 180.0F, 0.0F ); + while( !level.noCollision( stray.getBoundingBox() ) ) { + if( currentPos.getY() > center.getY() + radius ) { + stray.remove(); + return false; // Too high + } + currentPos.move( 0, 1, 0 ); + stray.moveTo( currentPos, stray.yRot, stray.xRot ); + } + + stray.setTarget( getTarget() ); + stray.finalizeSpawn( (IServerWorld) level, level.getCurrentDifficultyAt( blockPosition() ), + SpawnReason.MOB_SUMMONED, null, null ); + level.addFreshEntity( stray ); + stray.spawnAnim(); + return true; } /** Override to load data from this entity's NBT data. */ @@ -182,12 +257,4 @@ public class SnowCreeperEntity extends _SpecialCreeperEntity { public void readVariantSaveData( CompoundNBT saveTag ) { setPathfindingMalus( PathNodeType.WATER, PathNodeType.WALKABLE.getMalus() ); } - - /** Called to make the frost walker ice platform around this entity, as needed. */ - private void updateFrostWalker( BlockPos pos ) { - final boolean actualOnGround = onGround; - onGround = true; // Spoof the frost walker enchant requirement to be on the ground - FrostWalkerEnchantment.onEntityMoved( this, level, pos, 1 ); - onGround = actualOnGround; - } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java index 29804ad..3df472e 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java @@ -117,7 +117,7 @@ public class _SpecialCreeperEntity extends CreeperEntity implements IExplodingMo } /** Override to change this creeper's explosion power multiplier. */ - protected float getVariantExplosionPower( float radius ) { return radius * (isSupercharged() ? 3.5F : (isPowered() ? 2.0F : 1.0F)); } + protected float getVariantExplosionPower( float radius ) { return radius * (isSupercharged() ? 3.5F : isPowered() ? 2.0F : 1.0F); } /** Override to change this creeper's explosion. */ protected void makeVariantExplosion( float explosionPower ) { diff --git a/src/main/java/fathertoast/specialmobs/common/entity/enderman/FlameEndermanEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/enderman/FlameEndermanEntity.java index 7bf671b..1918fda 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/enderman/FlameEndermanEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/enderman/FlameEndermanEntity.java @@ -3,12 +3,20 @@ package fathertoast.specialmobs.common.entity.enderman; import fathertoast.specialmobs.common.bestiary.BestiaryInfo; import fathertoast.specialmobs.common.bestiary.MobFamily; import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.util.ExplosionHelper; import fathertoast.specialmobs.common.util.References; import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import net.minecraft.block.AbstractFireBlock; +import net.minecraft.block.Blocks; +import net.minecraft.block.FireBlock; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; import net.minecraft.entity.ai.attributes.Attributes; import net.minecraft.item.Items; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.Explosion; import net.minecraft.world.World; @SpecialMob @@ -51,13 +59,80 @@ public class FlameEndermanEntity extends _SpecialEndermanEntity { //--------------- Variant-Specific Implementations ---------------- + private int flameRingCooldown; + public FlameEndermanEntity( EntityType entityType, World world ) { super( entityType, world ); } + /** Called each tick to update this entity's movement. */ + @Override + public void aiStep() { + flameRingCooldown--; + super.aiStep(); + } + /** Override to apply effects when this entity hits a target with a melee attack. */ @Override protected void onVariantAttack( Entity target ) { target.setSecondsOnFire( 5 ); + if( target instanceof LivingEntity ) { + for( int i = 0; i < 64; i++ ) { + if( teleport() ) break; + } + } } - // TODO implement ability to create ring of fire around target on teleport + /** @return Teleports this enderman towards the target; returns true if successful. */ + @Override + protected boolean teleportTowards( Entity target ) { + if( super.teleportTowards( target ) ) { + if( flameRingCooldown <= 0 ) { + flameRingCooldown = 100 + random.nextInt( 100 ); + makeFireRing( target.blockPosition() ); + } + return true; + } + return false; + } + + /** Creates a ring of fire around the target position. */ + private void makeFireRing( BlockPos center ) { + if( ExplosionHelper.getMode( this ) == Explosion.Mode.NONE ) return; + + final int radius = 5; + final int rMinusOneSq = (radius - 1) * (radius - 1); + + for( int x = -radius; x <= radius; x++ ) { + for( int z = -radius; z <= radius; z++ ) { + final int distSq = x * x + z * z; + + // Attempt to place fire along circumference only + if( distSq <= radius * radius && distSq > rMinusOneSq ) { + placeFireWall( center.offset( x, 0, z ), radius ); + } + } + } + } + + /** Try to place a fire wall part at the location. */ + private void placeFireWall( BlockPos pos, @SuppressWarnings( "SameParameterValue" ) int radius ) { + final BlockPos.Mutable currentPos = pos.mutable(); + currentPos.setY( Math.max( pos.getY() - radius, 0 ) ); + final int maxY = Math.min( pos.getY() + radius, level.getMaxBuildHeight() - 2 ); + + while( currentPos.getY() < maxY ) { + currentPos.move( 0, 1, 0 ); + + if( shouldSetFire( currentPos ) ) + level.setBlock( currentPos, AbstractFireBlock.getState( level, currentPos ), References.SetBlockFlags.DEFAULTS ); + } + } + + /** @return True if a fire block can be placed at the position. */ + private boolean shouldSetFire( BlockPos pos ) { + if( !level.getBlockState( pos ).getMaterial().isReplaceable() ) return false; + if( ((FireBlock) Blocks.FIRE).canCatchFire( level, pos, Direction.UP ) ) return true; + + final BlockPos posBelow = pos.below(); + return level.getBlockState( posBelow ).isFaceSturdy( level, posBelow, Direction.UP ); + } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/enderman/RunicEndermanEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/enderman/RunicEndermanEntity.java index 26d99db..ab4cd04 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/enderman/RunicEndermanEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/enderman/RunicEndermanEntity.java @@ -6,10 +6,12 @@ import fathertoast.specialmobs.common.bestiary.SpecialMob; import fathertoast.specialmobs.common.entity.MobHelper; import fathertoast.specialmobs.common.util.References; import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import net.minecraft.block.Blocks; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.item.Items; import net.minecraft.potion.EffectInstance; import net.minecraft.potion.Effects; import net.minecraft.world.World; @@ -42,7 +44,8 @@ public class RunicEndermanEntity extends _SpecialEndermanEntity { @SpecialMob.LootTableProvider public static void buildLootTable( LootTableBuilder loot ) { addBaseLoot( loot ); - //TODO add drops + loot.addClusterDrop( "common", Blocks.STONE ); + loot.addRareDrop( "rare", Items.END_CRYSTAL ); } @SpecialMob.Factory @@ -68,4 +71,6 @@ public class RunicEndermanEntity extends _SpecialEndermanEntity { livingTarget.addEffect( new EffectInstance( Effects.LEVITATION, duration ) ); } } + + // NOTE would be fun to try and make this mob shoot an 'end crystal laser' to deal ranged damage and/or knockback } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/DesiccatedSilverfishEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/DesiccatedSilverfishEntity.java index 3238615..e4ca6ec 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/DesiccatedSilverfishEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/DesiccatedSilverfishEntity.java @@ -3,6 +3,8 @@ package fathertoast.specialmobs.common.entity.silverfish; import fathertoast.specialmobs.common.bestiary.BestiaryInfo; import fathertoast.specialmobs.common.bestiary.MobFamily; import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.config.species.DesiccatedSilverfishSpeciesConfig; +import fathertoast.specialmobs.common.config.species.SpeciesConfig; import fathertoast.specialmobs.common.entity.MobHelper; import fathertoast.specialmobs.common.util.References; import fathertoast.specialmobs.datagen.loot.LootTableBuilder; @@ -14,13 +16,19 @@ import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.ai.attributes.Attributes; import net.minecraft.fluid.Fluids; +import net.minecraft.nbt.CompoundNBT; import net.minecraft.potion.EffectInstance; import net.minecraft.potion.Effects; import net.minecraft.tags.FluidTags; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.Direction; +import net.minecraft.util.Tuple; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import java.util.ArrayDeque; +import java.util.Queue; + @SpecialMob public class DesiccatedSilverfishEntity extends _SpecialSilverfishEntity { @@ -37,6 +45,15 @@ public class DesiccatedSilverfishEntity extends _SpecialSilverfishEntity { .addToAttribute( Attributes.MAX_HEALTH, 4.0 ).addToAttribute( Attributes.ARMOR, 2.0 ); } + @SpecialMob.ConfigSupplier + public static SpeciesConfig createConfig( MobFamily.Species species ) { + return new DesiccatedSilverfishSpeciesConfig( species, DEFAULT_SPIT_CHANCE, 64, 64 ); + } + + /** @return This entity's species config. */ + @Override + public DesiccatedSilverfishSpeciesConfig getConfig() { return (DesiccatedSilverfishSpeciesConfig) getSpecies().config; } + @SpecialMob.LanguageProvider public static String[] getTranslations( String langKey ) { return References.translations( langKey, "Desiccated Silverfish", @@ -60,42 +77,20 @@ public class DesiccatedSilverfishEntity extends _SpecialSilverfishEntity { //--------------- Variant-Specific Implementations ---------------- - public DesiccatedSilverfishEntity( EntityType entityType, World world ) { super( entityType, world ); } + private int absorbCount; + + public DesiccatedSilverfishEntity( EntityType entityType, World world ) { + super( entityType, world ); + absorbCount = getConfig().DESICCATED.absorbCount.next( random ); + } /** Called each tick to update this entity's movement. */ @Override public void aiStep() { - clearWater( new BlockPos( position() ) ); + if( absorbCount > 0 && spongebob() ) spawnAnim(); super.aiStep(); } - private void clearWater( BlockPos pos ) { - if( !level.getFluidState( pos ).is( FluidTags.WATER ) ) return; - - final BlockState block = level.getBlockState( pos ); - - if( block.getBlock() instanceof IBucketPickupHandler && - ((IBucketPickupHandler) block.getBlock()).takeLiquid( level, pos, block ) != Fluids.EMPTY ) { - // Removed through bucket pickup handler - heal( 1.0F ); - return; - } - - if( block.getBlock() instanceof FlowingFluidBlock ) { - level.setBlock( pos, Blocks.AIR.defaultBlockState(), References.SET_BLOCK_FLAGS ); - heal( 1.0F ); - return; - } - - final Material material = block.getMaterial(); - if( material == Material.WATER_PLANT || material == Material.REPLACEABLE_WATER_PLANT ) { - final TileEntity tileEntity = block.hasTileEntity() ? level.getBlockEntity( pos ) : null; - Block.dropResources( block, level, pos, tileEntity ); - level.setBlock( pos, Blocks.AIR.defaultBlockState(), References.SET_BLOCK_FLAGS ); - heal( 1.0F ); - } - } - /** Override to apply effects when this entity hits a target with a melee attack. */ @Override protected void onVariantAttack( Entity target ) { @@ -110,4 +105,67 @@ public class DesiccatedSilverfishEntity extends _SpecialSilverfishEntity { /** @return This entity's creature type. */ @Override public CreatureAttribute getMobType() { return CreatureAttribute.UNDEAD; } + + /** @return Copy of the sponge absorption method, adapted for use by this entity. Returns true if anything is absorbed. */ + private boolean spongebob() { + final int initialCount = absorbCount; + final Queue> posToCheckAround = new ArrayDeque<>(); + tryAbsorb( posToCheckAround, -1, blockPosition() ); + + while( !posToCheckAround.isEmpty() && absorbCount > 0 ) { + final Tuple tuple = posToCheckAround.poll(); + final BlockPos rootPos = tuple.getA(); + final int rootDistance = tuple.getB(); + + for( Direction direction : Direction.values() ) { + tryAbsorb( posToCheckAround, rootDistance, rootPos.relative( direction ) ); + if( absorbCount <= 0 ) break; + } + } + + return initialCount > absorbCount; + } + + /** Attempts to absorb a single block position. If successful, marks the position to be checked around if it's in range. */ + private void tryAbsorb( Queue> posToCheckAround, int rootDistance, BlockPos pos ) { + if( !level.getFluidState( pos ).is( FluidTags.WATER ) ) return; + + // Prioritize bucket handler, then empty water block, then water plants + final BlockState block = level.getBlockState( pos ); + if( block.getBlock() instanceof IBucketPickupHandler && + ((IBucketPickupHandler) block.getBlock()).takeLiquid( level, pos, block ) != Fluids.EMPTY ) { + onAbsorb( posToCheckAround, rootDistance, pos ); + } + else if( block.getBlock() instanceof FlowingFluidBlock ) { + level.setBlock( pos, Blocks.AIR.defaultBlockState(), References.SetBlockFlags.DEFAULTS ); + onAbsorb( posToCheckAround, rootDistance, pos ); + } + else if( block.getMaterial() == Material.WATER_PLANT || block.getMaterial() == Material.REPLACEABLE_WATER_PLANT ) { + final TileEntity tileEntity = block.hasTileEntity() ? level.getBlockEntity( pos ) : null; + Block.dropResources( block, level, pos, tileEntity ); + level.setBlock( pos, Blocks.AIR.defaultBlockState(), References.SetBlockFlags.DEFAULTS ); + onAbsorb( posToCheckAround, rootDistance, pos ); + } + } + + /** Called when a block is actually absorbed. Marks the position to be checked around if it's in range. */ + private void onAbsorb( Queue> posToCheckAround, int rootDistance, BlockPos pos ) { + if( rootDistance < 6 ) posToCheckAround.add( new Tuple<>( pos, rootDistance + 1 ) ); + + heal( 1.0F ); + absorbCount--; + } + + /** Override to save data to this entity's NBT data. */ + @Override + public void addVariantSaveData( CompoundNBT saveTag ) { + saveTag.putByte( References.TAG_AMMO, (byte) absorbCount ); + } + + /** Override to load data from this entity's NBT data. */ + @Override + public void readVariantSaveData( CompoundNBT saveTag ) { + if( saveTag.contains( References.TAG_AMMO, References.NBT_TYPE_NUMERICAL ) ) + absorbCount = saveTag.getByte( References.TAG_AMMO ); + } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/FireSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/FireSpiderEntity.java index 9d974d6..b34031b 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/spider/FireSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/FireSpiderEntity.java @@ -60,7 +60,7 @@ public class FireSpiderEntity extends _SpecialSpiderEntity { if( !level.isClientSide() && target instanceof LivingEntity ) { final BlockPos pos = target.blockPosition(); if( level.getBlockState( pos ).getMaterial().isReplaceable() ) { - level.setBlock( pos, Blocks.FIRE.defaultBlockState(), References.SET_BLOCK_FLAGS ); + level.setBlock( pos, Blocks.FIRE.defaultBlockState(), References.SetBlockFlags.DEFAULTS ); } } target.setSecondsOnFire( 5 ); diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/WebSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/WebSpiderEntity.java index 9f0dee1..fef7b5b 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/spider/WebSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/WebSpiderEntity.java @@ -97,7 +97,7 @@ public class WebSpiderEntity extends _SpecialSpiderEntity { /** @return Attempts to place a cobweb at the given position and returns true if successful. */ private boolean tryPlaceWeb( BlockPos pos ) { if( level.getBlockState( pos ).getMaterial().isReplaceable() ) { - level.setBlock( pos, Blocks.COBWEB.defaultBlockState(), References.SET_BLOCK_FLAGS ); + level.setBlock( pos, Blocks.COBWEB.defaultBlockState(), References.SetBlockFlags.DEFAULTS ); webCount--; return true; } diff --git a/src/main/java/fathertoast/specialmobs/common/util/References.java b/src/main/java/fathertoast/specialmobs/common/util/References.java index 8be7fa1..885666f 100644 --- a/src/main/java/fathertoast/specialmobs/common/util/References.java +++ b/src/main/java/fathertoast/specialmobs/common/util/References.java @@ -1,10 +1,12 @@ package fathertoast.specialmobs.common.util; import fathertoast.specialmobs.common.core.SpecialMobs; +import net.minecraft.block.BlockState; import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.ListNBT; import net.minecraft.nbt.StringNBT; +import net.minecraft.util.math.BlockPos; import java.util.UUID; @@ -29,13 +31,32 @@ public final class References { //public static final String TEXTURE_SHOOTING_EYES_SUFFIX = "_shooting_eyes"; - //--------------- BIT FLAGS ---------------- - - public static final int SET_BLOCK_FLAGS = 0b00000011; - - //--------------- EVENT CODES ---------------- + /** Bit flags that can be provided to {@link net.minecraft.world.World#setBlock(BlockPos, BlockState, int)}. */ + @SuppressWarnings( "unused" ) + public static class SetBlockFlags { + /** Triggers a block update. */ + public static final int BLOCK_UPDATE = 0b0000_0001; + /** On servers, sends the change to clients. On clients, triggers a render update. */ + public static final int UPDATE_CLIENT = 0b0000_0010; + /** Prevents clients from performing a render update. */ + public static final int SKIP_RENDER_UPDATE = 0b0000_0100; + /** Forces clients to immediately perform the render update on the main thread. Generally used for direct player actions. */ + public static final int PRIORITY_RENDER_UPDATE = 0b0000_1000; + /** Prevents neighboring blocks from being notified of the change. */ + public static final int SKIP_NEIGHBOR_UPDATE = 0b0001_0000; + /** Prevents neighbor blocks that are removed by the change from dropping as items. Used by multi-part blocks to prevent dupes. */ + public static final int SKIP_NEIGHBOR_DROPS = 0b0010_0000; + /** Marks the change as the result of a block moving. Generally prevents connection states from being updated. Used by pistons. */ + public static final int IS_MOVED = 0b0100_0000; + /** Prevents light levels from being recalculated when set. */ + public static final int SKIP_LIGHT_UPDATE = 0b1000_0000; + + /** The set block flags used for most non-world-gen purposes. */ + public static final int DEFAULTS = BLOCK_UPDATE | UPDATE_CLIENT; + } + // Entity events; used in World#broadcastEntityEvent(Entity, byte) then executed by Entity#handleEntityEvent(byte) public static final byte EVENT_TELEPORT_TRAIL_PARTICLES = 46; @@ -64,12 +85,6 @@ public final class References { // public static final String TAG_TEXTURE = "Texture"; // public static final String TAG_TEXTURE_EYES = "TextureEyes"; // public static final String TAG_TEXTURE_OVER = "TextureOverlay"; - public static final String TAG_ARROW_DAMAGE = "ArrowDamage"; - public static final String TAG_ARROW_SPREAD = "ArrowSpread"; - public static final String TAG_ARROW_WALK_SPEED = "ArrowWalkSpeed"; - public static final String TAG_ARROW_REFIRE_MIN = "ArrowRefireMin"; - public static final String TAG_ARROW_REFIRE_MAX = "ArrowRefireMax"; - public static final String TAG_ARROW_RANGE = "ArrowRange"; public static final String TAG_FALL_MULTI = "FallMulti"; public static final String TAG_FIRE_IMMUNE = "FireImmune"; public static final String TAG_BURN_IMMUNE = "BurningImmune"; @@ -80,6 +95,12 @@ public final class References { public static final String TAG_WATER_DAMAGE = "WaterDamage"; public static final String TAG_STICKY_IMMUNE = "StickyImmune"; public static final String TAG_POTION_IMMUNE = "PotionImmune"; + public static final String TAG_ARROW_DAMAGE = "ArrowDamage"; + public static final String TAG_ARROW_SPREAD = "ArrowSpread"; + public static final String TAG_ARROW_WALK_SPEED = "ArrowWalkSpeed"; + public static final String TAG_ARROW_REFIRE_MIN = "ArrowRefireMin"; + public static final String TAG_ARROW_REFIRE_MAX = "ArrowRefireMax"; + public static final String TAG_ARROW_RANGE = "ArrowRange"; // Creepers public static final String TAG_SUPERCHARGED = "Supercharged"; @@ -113,7 +134,7 @@ public final class References { // Misc. public static final String TAG_FUSE_TIME = "FuseTime"; // Blackberry Slime, Volatile Magma Cube - public static final String TAG_AMMO = "Ammo"; // Web (Cave) Spider, Mad Scientist Zombie + public static final String TAG_AMMO = "Ammo"; // Web (Cave) Spider, Mad Scientist Zombie, Desiccated Silverfish public static final String TAG_IS_FAKE = "IsFake"; // Mirage Enderman public static final String TAG_EXPLOSION_POWER = "ExplosionPower"; // Hellfire Blaze diff --git a/src/main/resources/assets/specialmobs/textures/entity/creeper/sand.png b/src/main/resources/assets/specialmobs/textures/entity/creeper/sand.png index 474601b18df86e40fc0e675b6594e1c30c765ae2..28b319cb9b32ca8123f00fe4fe91da5f85c5722b 100644 GIT binary patch delta 581 zcmV-L0=oV31p5S#L4O-iOjJeV+pyZwqt(Qg(YlJpu5s<_%je{~vYBT9008o*v-to3 z010$bPE-H?|NsC0|Nj6}Pk0gl000SaNLh0L01mh0?Eio9Ju5Wnj5WROPE(=*#9h=L2{X6i#cOT+T7S#wkGK*L>|U&f{XPLv zrl*FjIsFkY2}&Wfw6NbNVA)wg`?Uf%RG_!8KXEXNrvlj@R45e!%|gZc{{$Tz$Fa5P zw}o}KtOnZI^xJ|2c+o(8jN)TlW2B_1K38yvJ)}F@fctTc$)cu8zb))QzZv1i`sU9u zRtY|DLEe(jsDFQqXo47rO}{P7&`HQng~(>+-@Til%g%q84z*I#SZQh zbb7Y;rrviRSeR!Jv04=COIBiW5lbUVnLbQ5r(o}Zm9Ey+LD2I#wmc+Cto+@Zr@go)PE|#|yP%(`~ z;K{=NUOre8h(y&OaV8Iwf-fpSjjqljwp%H>T%}KbUa))+1zGv?pTFI`@Obl0L1o8xsL4O!fOjJeV+pyZwqt(Qg(YlJpu5s<_%je{~0002f|D~h=000Pd zQchF<|NsC0|Nj8qgS}Y*000SaNLh0L01m?d01m?e$8V@)0005cNklU57Xuwuj z@fXuBY8w)8fLO&M9W14Bs6>jNn{1`ISV`@iy0XB4uLSEsD@VqP*B6u#W5tq`R%cVybK0B%p3mvFLCqoI;DQ| l1S11OUT*qtpc)2-`yO&k(yYICcqI3OxSp2wZsw_|1(&=Frb|1 z4LBYf=)l21`sWGKpc9}R`cKe-Zw6nzM2QLaUw=kWGl5Xl@s(hqpd<85zZzDG?ogH} z1^1#x8?10Me?l@+Uxau8upqeOP4g#clhB!EeMM|Q#JVEMfl{K)`MlPW0`*)2D%PDHLkV1figjH1i%E4L4O!fOjJcZF)t+@A4fbnV^d0EQ%ZhiSpWb30001mZF2;Sa>^M0Gk-{57*I}(1{~W4I&d(>ZsbRZOEd?eT?$OzrjuZE4H8OoAN!CaK6zzCE1 z3xW~)BIE;r1;L7o<}Xl@(3#~ta;`wm=ZQ`dIs$>0`3oeDLke4{*L77*Y=Ew;Vlsb$ z3<7)tC%Q)q8GrY|4y28u`3s^9fUFk0f2#qS?su@hDsQuqzh5KayaQ(oP6bmtradtR zSU(%6m-b}h)iM&q7$7dlP6cjocQo+U`a|K<<8UgF39egc2He^(5Rqcar_dYbb+n?> zb`nQJ%VIUJLk9p(d9Q2~L#;*dj4cBRo6dn|uVzkQ1%H2xi*zx&E`mUWmb{Rphr`3P zu6eZelL77~R8pvArwm!N;)S6Dvb`vL^3mXkr3bS?khS)J9jLt!6bNgD-v4V*q>n>1 zLgYYV0wLJ^pdR>~RD4$OJ)oZ4rihEG9t{RV(lG9jACj((OUU^L_0=7MC~;#L00000 MNkvXXt^-0~f|&E>bpQYW diff --git a/src/main/resources/assets/specialmobs/textures/entity/enderman/flame_eyes.png b/src/main/resources/assets/specialmobs/textures/entity/enderman/flame_eyes.png index 2a46d11eff2d22f40c94e476d98240e5bbb3c034..da8232ef6b5a9f092437f9e7254633bdbb6999f3 100644 GIT binary patch delta 178 zcmaFKc%N~CBnKM<1H+=ZbuAMW73w()JR*x382Ao?FyoGi*>8c8C7v#hAsNnZZye-o zFyLu@c)|3}Y0WvOaz*AGT>Rjupw@bq6#@qrPy63|$>inI<95tIEe$73mh9<^wVP&F zeL7_I@|oL#Vhjup%3q#;|8#M-b^Np%{ysgBnLAC1H)A3zHJi~73%o|d_r7L<}lq~B>nzu*mPfAkX#YpMIgmm666>B z9|#zJZ{rIF3UC&9L>4nJ@ErnSMo|r~2B2V&r;B5VhxgmdmV68f94>*=cK)Avv?VO2 zgZE+RjhbH0mL+^839CdD#2kKDMZDyXD_v04;Ckcu0jq@DrJJHP7Jygla~+%LBaA$lv-;6DLz00@KSo!K7U4(_74Z6_WB10q6@o& z8f1~;EtrdIchJdU^qS%=rUR~&Uxn#Mjpq@RpAK4yUZ1al$@{ixtbI)J7MRWm#8U|{ z){-=Pv6g@F*`aM_48iJk?LceVh!H5@kJ_StUK7*pfXvy!I=*=8)wP%%z2qAQ0)&ul z1hMnxQ9R0p$bTxe_B%|L3z=c~jzIB{e5)jM=fF_Xq@l?RVOWgel_8XS2XLK7>Ssmv z9*tBgtC;Ewk`CafZXy4K_B2%&q|KGAY7kO1(sbdZv&DwwpB+$PEIBWK&6#@$Y_Y+% z7^i4RA?@xURoCWGIqOc*kR`Sm!r?%w2M%l~vgoH_Du2>$;rN-N9nc|~+HFSFt8Fyo z#hrEwo&cn%T%&iL6cmN(+XY6^P5L7T6etckLD tw~pyuN}63r))~7-ZsZ}KHJE=}`v&AsA$75E*8%_l002ovPDHLkV1jdeCyM|8 delta 578 zcmV-I0=@l}1^5JzL4OiZOjJdGe}9jUk7#IU|NsAVbaVg!0Oy0xmH+?%26R$RQ~&?} z|Nj89zofqa000SaNLh0L01m?d01m?e$8V@)0005qNklYunV-QQN;WR^`+|~ zV6>PYQ3ASRzYcXj+I*hD@m-)FbF}AkVDi3fv{}cPAED6703IcvHdE5AVz2(q-vuFg z))nQ+Q@eLuW6B}E#xqqJiNC>9F2H96S%iA=Y2}yLyA0H)GGOY7om1K9t%rOV3v#Np zpF!`yVXO1JB!4S`rUAVXpm)GvXHrnw6obq;YG&RD)JzNFddA{k6*;?xqI<@DL+FC| z@tw$@$(qJ>Lt^e^ssY3_5_;m|bHs-5uL7zt6`o%Hn&T1#j@aN>1;;dS;N1n{x^EuJ znLDOIq+*l862SF9z=4pVUz%|w?!@{T(*i15x@P0B^MBYjH1*P*xD)O`h{;%~DN$4u zRo|4!qCXN)fl7eF5<~ra#}=E>-Xs8j>Z?sI7DY)KYFCYGyCRK>2{$ai2hOHqmwFPa z!Ux<(0#nC~`IUiBPB|n1os=r@JGKlU``qwSxM2Zja0;9{%iaZ&cC96oD9ag;(MDL0 z?aL%;6gz-Z(+vSKaG(-%SROq5T5i4^K3+N>gp^Vc))l)485HuIjr!aB7cs3F0A0j2 QZU6uP07*qoM6N<$f_M-R!TjgXpAl4T)TpBEx0nKJ(&shR6KCRiRPz+$M9e`O5Ku>t0S^z`UXW5Omk@s8Vk3dOeDFasyd{o$j&pqSg-=6t-)SV0)Ul!JV%v^0n9o8jB)@u^-rbn zs}5ME0&lT*kQ4(je_EvJ3j**8?1#rw%s26%hk({zvV0FJ+#jh600000NkvXXu0mjf D>y~U8 diff --git a/src/main/resources/assets/specialmobs/textures/entity/silverfish/desiccated.png b/src/main/resources/assets/specialmobs/textures/entity/silverfish/desiccated.png index 752616bedebed8d6f08242c77a10ce1b1f4d4a6d..f407f15d1afd4e58b20ea1145d85f7c169ef9490 100644 GIT binary patch delta 685 zcmX@e@`!bUWBr5xpAgr|IjMzHqchvXEP~W--@ZL-)~qvU&Kx~@l!1Z4qtxi=;ltKR z`uq0m*|~kYXQ8QIwb}nS{$J0i+hywhyJq?STkpPo``X*vr%ju-ckkXOPo7MfGUddH z6K~(Xy?F8Bnl)>-Y}$11&Ycs-j$J%|e&)=Xdw1_%vS`tY6)To4Su%fq{rtwp#-5&@ z2@@vFm@#Af_U(%oF9zBw{gvSakTNI<@(cb?8em9TdZ7ZSm$SelvY3H^?+^$xifVW@ zFfcIA_jGX#@$h~-<#k@O0Z%LQk@_`jr*Bvmz5Dmp-DT1D{=Y4gVcOEPaKYcZ?Jgb4 z^N;lPeu_)23>1mGFiWFm33H=TbG>fBH22A@pOU7X@6wrnzIJCq+?3;LdP^5Ri=3Bg z`RDmXBf*x0)u*;O&e1unkTJhVjKd;3HvT|`^gJouHpeNeZ){wxvw3sf)~I!1%ag8u zs6X0N!4|P))=4{o4IkDi9MZeRBy)VjM-QE@wZ%PeCw3f5pRahE@kFD2bVJeJg`d~; z@O|$wvMCS}+@aC9%zM%5bv;t^tA5A|YHYoB-P~on$<7Ii3GaAzOuN3fa)RPIl|^SB zPo5ZLlyWBQ(@&Xjp}>?Eu3nuVHcm~d=+%yub3ebN{@%Tp&1PHf3BI-4|H671qv_=* z{Eq`zT~#_Q%7ahJ_U?9LHDk4H;dpSTmhnQ|!yB<(4XK&Ss_Z16-dHs80zZe!EwzP5 zE}#7BsCJ7%DF1lS^@TF;0%mc(|C{&ZgNlOX1oo{bz9O^H>p3umKc@44$rjF6*2UngDv8 BYIgtt delta 486 zcmVaxPv;Q;000SaNLh0L01m?d01m?e$8V@)0004Q zNklym>Y427eD)eyIFdH;9a6E4Db$CTH%A!Wfs!h1j<#dZraPqp$vrq}=yFITC1L5dpMW5VzL zHFEkIupHPQQeJH|9iB)6$YEZ+b5*^KT*iYydq0yRc19+p>AWKZcO#_GUPH0m#6tKJ c{{k`o0Oi_>eE(hEfdBvi07*qoM6N<$f@XQzl>h($ diff --git a/src/main/resources/assets/specialmobs/textures/entity/spider/fire.png b/src/main/resources/assets/specialmobs/textures/entity/spider/fire.png index d6631d68441c9487deae573ef496bf1be5acd111..4dc3af2bee0b8664117f00830137f0690f385f31 100644 GIT binary patch delta 718 zcmV;<0x|u`2EGQ6Pa?@|0^gz=G$kilL`PywPIXyY@6A@1O)V!H8vp@HE|9ht=yU?_&ff8a%KB3#gvTbGK(or6dQd_B8sa21) zb8oe7^?D9WND`D%i{WD7DF9-rr3+Y_uzF;(S6SThB#^d=^*Ok#VD z;e{X>%Jx%=;*5}SUZ0eXN%_@J0_#kFKn?yAX&@w#F~*(7lOSrq`Het87y>_T0&(Kp z^_TXg7pn+3b(JD?T?nWZSfbAr>(pN&v~Y85Llp9u+cyEOwQ?-1Q-2BZ&pC#>DXZLu z;9}6i$#|XmOXL>(w3RG!n;Hn@E!7Wd6MWWS?!fm6K-WT$W2l>Tu}=LZo4|p856SIk z8$(=TR~br#S(M|c1Udv7LY_v98L|+lz$iHweIlu4lsc9FKHI)`-N<-q(}~DIVlDP9 z2FMwG4)$bxD1cq((cLWL(XIr>D_rN0GiBR3dz(i;I(Ih$0y|m>CPE!>F1%AE{(aWH zx8C;3+N@T}EU}g#AdF1V?rYh9oBTQh-CEnDIuYSYK%-S46%7I+m02mXoptnMj-!uy zwkZO<+vF{Rp>c^Ipx0@$`g@+OAClhA^EdXQ%WqvUS{x8W5FZ2eE-Qfsfh_0&VZxBA zud)ybH+f~r-8E7TQ}tEW2oSvcM+ACI)mPDsFzY0bxxN|TiNp06HTLy?Dr-6tv&aTq z9@S*kUL)?&6_mCc%pO!4@x&~m2zDF;;bTPrLZnw(A`&^#x&9+PMQECrz3~=1v!CM}WKhC}{59ug#5C8xG07*qoM6N<$g4^;; Aj{pDw delta 729 zcmV;~0w(>w2FV7HPa>C1Ey-*G-=Z7u%~mueCs{;CVoXkTSz0F=8vpTz+5G8?maAl-_w@{Kt4`efedhnlM!KW1b#u%?M9t5ig`hFt- zD29a}F9J9f-2KP;wimkqoLsenj*URQ5?0ygO6#;g1vI!h%7B81seTbCG%M$#b=sez z{O24)O^{crMet-`;bOc_`%?%j{InMysa6jFSw;UuZGyi&n3v$!6o6|Z$T8%9X4_h) z{V4_T!Y@hnV;h&a#qP2wB@qP;xidy0V2t!MP^%JTBcQ=D>QiZ!GKo5BOzOX8yRObn z6;G5dgaC=PI5rF-$D|GM_~3k!z{U6M-Ynx~-3g2*UDuE^W#2h_n`b|}?%oIyIM7Zo zCF+BMgLkRQ|C(|2*4q_Yo7F~t+!AY91WF?dboi)OBd#mp*4nkYvohQXw9zz(rinnA z;#TWyM~r^XarO~Mn-D0vT|P1xjLS+w^nC3WKgZGfiS%|HKTEfE`C}K1hC>7^i9ZAN zDLVm!penQnN>dDJ`YIa%xTzzn+TA01n5M6?XCP5@|3JWFn!XA%qFGme@p9*#A-wW% zKSnG)N15qJ%qB%}BC^T!o)P!x0@bz~ES;zr@x*LG2A2X0qL-Bt0FhC(C6Ew=@BWYU z5KgO{*BR2K`rjEim^#ASd+=Ry*xh-WmLCR4$7-oX=P3UZl%jqKu6^TJ?QQcxRKSE@bYt!rxB`>w1~CDdm|cUQKyZLmUO?UmRD?JICV|Tm6g;y8OOW}z bKy?F-Dj!WQ4>XW=00000NkvXXu0mjfPEbXi delta 172 zcmV;d08{^v0*?ZaMiZA!Ey-*G-=Z7u%~k*a01-28Z;@Fke|SkmK~zXf?UP#yfFKY> z=e7SecakmgsDq71=oZi1c}cJ a%r-9OA4xt!4w@YR0000(L_t(YOLditqJkh0LVSoZ&4d(RALSFKG2mz`l>*z~qcA*L|J#4oy<@s;1Tn{Cf+T|5!vL&jO=pdFUKus*m@b1Lq?i+4Ao1R%7-Lv0T>1}18UJ|FA56yfE8tgskrH*zA7pX7QXlk5BflOrE+VX6# z<+W&RokH8=VmcAzh5&)U0ebr!OIXn>0){n| zo}KRDB?lNP2O^NCn8n!EHCIUL8fsqGrcQB)m>dZBsP!C#o+B=s<(0>@*0$>GtzD-Y z%GrUn1j?8*F^Zr&acQWtoCxR<0?k4B6~XmPoaLBLoe}>7yyN|}wPdO4yMTsxu zfe`|D#?ntw&(Xc=2ua}7mf)#UVGd00OE>L_t(YOLdit!m1z;gaxv=SoZ!;d%hXae)2S_AnXjg%VNvhSVBx;iHTo) zzi;c^*LvgeaJ#Nt$9v~=90D=NWPv1s+rtE`S9MplcV21L?VOGw5K_zuFF?F^E3IrI zGLSVWUnKq4hd%-_L@C6_fBj~bUA8BZ>>53|^(ruOpa=-ze_$pPX38;_Qu1ka2oOKF za`;OCq7*v6d;}bJ|LJkOU1T6u?igX`Nq{whxMxrOF@K^F0o0ZX5+rO4OMnRl?;!1@ z`(yqD0@6;$NY&VzC4f>7GqdBfaD?Em$U`0e>rHS7Op07xj2R` z01=WJ&-MP8KapyyT;dvI_}IpdrW7m73fRawX++&k0#66bNOIg@5?G@AL|`G5?p2OO zl*r=G);1bNTp4sAInM%lVeLlawY4EGABn#&Zt^(7pf z33$-7mMVx3-Y8~OvR2grMnoS1eGvy|!r~RMRv-llgZD#0XIY6=Q4Y=ohw6`&3R_nC zQ9wwVZmn^qQ3q!NrG=sC+BaGZT>@tIy|7O_2WP^fBjdjczA`pem2!JXJdKXe%iv5< pabqR)W&NB7QIN*xJNUu-{SWA#NWIa%3mO0b002ovPDHLkV1mC8R&4+P diff --git a/src/main/resources/assets/specialmobs/textures/entity/spider/web_eyes.png b/src/main/resources/assets/specialmobs/textures/entity/spider/web_eyes.png index ed2af031e61aca93f74e805afc2243a481d95363..a686aaefd23839214ac30c11269d2a1c72e4fda3 100644 GIT binary patch delta 39 ucmeyv^oMDJr%Y6Qd`4lOUx2T>n{#kRdr*3tptw8(14FRd(`Oq~#Tfw`tPMs0 delta 39 vcmeyv^oMDJr%Y3O`-GX($|_3p^Rk08+Jn;D1jXeU7#M1*U92{yiZcQLN!1Ql From 2a221eaddc9d60261d3ff8858e0e43b9bf686a8e Mon Sep 17 00:00:00 2001 From: FatherToast Date: Sun, 24 Jul 2022 00:12:12 -0500 Subject: [PATCH 16/16] cave bois --- .../common/bestiary/MobFamily.java | 4 +- .../cavespider/DesertCaveSpiderEntity.java | 77 ++++++++++++++ .../cavespider/FireCaveSpiderEntity.java | 68 ++++++++++++ .../cavespider/PaleCaveSpiderEntity.java | 70 ++++++++++++ .../cavespider/WaterCaveSpiderEntity.java | 100 ++++++++++++++++++ .../entity/spider/WaterSpiderEntity.java | 2 +- .../textures/entity/cave_spider/desert.png | Bin 0 -> 974 bytes .../entity/cave_spider/desert_eyes.png | Bin 0 -> 209 bytes .../textures/entity/cave_spider/fire.png | Bin 0 -> 830 bytes .../textures/entity/cave_spider/fire_eyes.png | Bin 0 -> 272 bytes .../textures/entity/cave_spider/pale.png | Bin 0 -> 897 bytes .../textures/entity/cave_spider/pale_eyes.png | Bin 0 -> 225 bytes .../textures/entity/cave_spider/water.png | Bin 0 -> 887 bytes .../entity/cave_spider/water_eyes.png | Bin 0 -> 220 bytes .../textures/entity/cave_spider/web_eyes.png | Bin 380 -> 380 bytes .../textures/entity/spider/web_eyes.png | Bin 380 -> 380 bytes 16 files changed, 318 insertions(+), 3 deletions(-) create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/cavespider/DesertCaveSpiderEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/cavespider/FireCaveSpiderEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/cavespider/PaleCaveSpiderEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/cavespider/WaterCaveSpiderEntity.java create mode 100644 src/main/resources/assets/specialmobs/textures/entity/cave_spider/desert.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/cave_spider/desert_eyes.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/cave_spider/fire.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/cave_spider/fire_eyes.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/cave_spider/pale.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/cave_spider/pale_eyes.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/cave_spider/water.png create mode 100644 src/main/resources/assets/specialmobs/textures/entity/cave_spider/water_eyes.png diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java index 48ba7a8..f34ac3a 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java @@ -81,8 +81,8 @@ public class MobFamily { ); public static final MobFamily CAVE_SPIDER = new MobFamily<>( FamilyConfig::newMoreSpecial, "CaveSpider", "cave spiders", 0x0C424E, new EntityType[] { EntityType.CAVE_SPIDER }, - "Baby", /*"Desert", "Fire",*/ "Flying", "Mother", /*"Water",*/ "Web", "Witch" - );//TODO desert, fire, water + "Baby", "Desert", "Fire", "Flying", "Mother", "Pale", "Water", "Web", "Witch" + ); public static final MobFamily SILVERFISH = new MobFamily<>( SilverfishFamilyConfig::new, "Silverfish", "silverfish", 0x6E6E6E, new EntityType[] { EntityType.SILVERFISH }, diff --git a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/DesertCaveSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/DesertCaveSpiderEntity.java new file mode 100644 index 0000000..2881174 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/DesertCaveSpiderEntity.java @@ -0,0 +1,77 @@ +package fathertoast.specialmobs.common.entity.cavespider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.config.Config; +import fathertoast.specialmobs.common.entity.MobHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.item.Items; +import net.minecraft.potion.EffectInstance; +import net.minecraft.potion.Effects; +import net.minecraft.world.World; + +@SpecialMob +public class DesertCaveSpiderEntity extends _SpecialCaveSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { + bestiaryInfo.color( 0xE6DDAC ).theme( BestiaryInfo.Theme.DESERT ) + .uniqueTextureWithEyes() + .size( 0.6F, 0.7F, 0.5F ) + .addExperience( 2 ) + .addToAttribute( Attributes.MAX_HEALTH, 4.0 ); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Desert Cave Spider", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.LEATHER ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return DesertCaveSpiderEntity::new; } + + /** @return This entity's mob species. */ + @SpecialMob.SpeciesSupplier + @Override + public MobFamily.Species getSpecies() { return SPECIES; } + + + //--------------- Variant-Specific Implementations ---------------- + + public DesertCaveSpiderEntity( EntityType entityType, World world ) { super( entityType, world ); } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( target instanceof LivingEntity ) { + final LivingEntity livingTarget = (LivingEntity) target; + final int duration = MobHelper.getDebuffDuration( level.getDifficulty() ); + + if( Config.MAIN.GENERAL.enableNausea.get() ) { + livingTarget.addEffect( new EffectInstance( Effects.CONFUSION, duration, 0 ) ); + } + livingTarget.addEffect( new EffectInstance( Effects.BLINDNESS, duration, 0 ) ); + + livingTarget.addEffect( new EffectInstance( Effects.MOVEMENT_SLOWDOWN, duration, 2 ) ); + livingTarget.addEffect( new EffectInstance( Effects.DAMAGE_RESISTANCE, duration, -3 ) ); // 40% inc damage taken + } + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/FireCaveSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/FireCaveSpiderEntity.java new file mode 100644 index 0000000..acdb4a3 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/FireCaveSpiderEntity.java @@ -0,0 +1,68 @@ +package fathertoast.specialmobs.common.entity.cavespider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import net.minecraft.block.Blocks; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.Items; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +@SpecialMob +public class FireCaveSpiderEntity extends _SpecialCaveSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { + bestiaryInfo.color( 0xDFA21B ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.FIRE ) + .uniqueTextureWithEyes() + .addExperience( 2 ).fireImmune().waterSensitive(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Cave Firefang", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.FIRE_CHARGE ); + loot.addUncommonDrop( "uncommon", Items.COAL ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return FireCaveSpiderEntity::new; } + + /** @return This entity's mob species. */ + @SpecialMob.SpeciesSupplier + @Override + public MobFamily.Species getSpecies() { return SPECIES; } + + + //--------------- Variant-Specific Implementations ---------------- + + public FireCaveSpiderEntity( EntityType entityType, World world ) { super( entityType, world ); } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( !level.isClientSide() && target instanceof LivingEntity ) { + final BlockPos pos = target.blockPosition(); + if( level.getBlockState( pos ).getMaterial().isReplaceable() ) { + level.setBlock( pos, Blocks.FIRE.defaultBlockState(), References.SetBlockFlags.DEFAULTS ); + } + } + target.setSecondsOnFire( 5 ); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/PaleCaveSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/PaleCaveSpiderEntity.java new file mode 100644 index 0000000..e787d36 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/PaleCaveSpiderEntity.java @@ -0,0 +1,70 @@ +package fathertoast.specialmobs.common.entity.cavespider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.MobHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.item.Items; +import net.minecraft.potion.EffectInstance; +import net.minecraft.potion.Effects; +import net.minecraft.world.World; + +@SpecialMob +public class PaleCaveSpiderEntity extends _SpecialCaveSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { + bestiaryInfo.color( 0xDED4C6 ).theme( BestiaryInfo.Theme.ICE ) + .uniqueTextureWithEyes() + .addExperience( 1 ) + .addToAttribute( Attributes.ARMOR, 15.0 ); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Pale Cave Spider", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addSemicommonDrop( "semicommon", Items.FERMENTED_SPIDER_EYE ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return PaleCaveSpiderEntity::new; } + + /** @return This entity's mob species. */ + @SpecialMob.SpeciesSupplier + @Override + public MobFamily.Species getSpecies() { return SPECIES; } + + + //--------------- Variant-Specific Implementations ---------------- + + public PaleCaveSpiderEntity( EntityType entityType, World world ) { super( entityType, world ); } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( target instanceof LivingEntity ) { + final LivingEntity livingTarget = (LivingEntity) target; + final int duration = MobHelper.getDebuffDuration( level.getDifficulty() ); + + livingTarget.addEffect( new EffectInstance( Effects.WEAKNESS, duration, 0 ) ); + livingTarget.addEffect( new EffectInstance( Effects.DIG_SLOWDOWN, duration, 1 ) ); + } + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/WaterCaveSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/WaterCaveSpiderEntity.java new file mode 100644 index 0000000..bc24207 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/WaterCaveSpiderEntity.java @@ -0,0 +1,100 @@ +package fathertoast.specialmobs.common.entity.cavespider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.MobHelper; +import fathertoast.specialmobs.common.entity.ai.AIHelper; +import fathertoast.specialmobs.common.entity.ai.FluidPathNavigator; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootEntryItemBuilder; +import fathertoast.specialmobs.datagen.loot.LootPoolBuilder; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.fluid.Fluid; +import net.minecraft.item.Items; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.pathfinding.PathNavigator; +import net.minecraft.pathfinding.PathNodeType; +import net.minecraft.tags.FluidTags; +import net.minecraft.world.World; + +@SpecialMob +public class WaterCaveSpiderEntity extends _SpecialCaveSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) { + bestiaryInfo.color( 0x2D41F4 ).theme( BestiaryInfo.Theme.WATER ) + .uniqueTextureWithEyes() + .addExperience( 1 ).drownImmune().fluidPushImmune() + .addToAttribute( Attributes.ATTACK_DAMAGE, 1.0 ); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Water Cave Spider", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addPool( new LootPoolBuilder( "common" ) + .addEntry( new LootEntryItemBuilder( Items.COD ).setCount( 0, 2 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) + .toLootPool() ); + loot.addPool( new LootPoolBuilder( "semicommon" ) + .addEntry( new LootEntryItemBuilder( Items.SALMON ).setCount( 0, 1 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) + .toLootPool() ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return WaterCaveSpiderEntity::new; } + + /** @return This entity's mob species. */ + @SpecialMob.SpeciesSupplier + @Override + public MobFamily.Species getSpecies() { return SPECIES; } + + + //--------------- Variant-Specific Implementations ---------------- + + public WaterCaveSpiderEntity( EntityType entityType, World world ) { + super( entityType, world ); + setPathfindingMalus( PathNodeType.WATER, PathNodeType.WALKABLE.getMalus() ); + } + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + AIHelper.replaceWaterAvoidingRandomWalking( this, 0.8 ); + } + + /** @return A new path navigator for this entity to use. */ + @Override + protected PathNavigator createNavigation( World world ) { + return new FluidPathNavigator( this, world, true, false ); + } + + /** @return Whether this entity can stand on a particular type of fluid. */ + @Override + public boolean canStandOnFluid( Fluid fluid ) { return fluid.is( FluidTags.WATER ); } + + /** Called each tick to update this entity. */ + @Override + public void tick() { + super.tick(); + MobHelper.floatInFluid( this, 0.06, FluidTags.WATER ); + } + + /** Override to load data from this entity's NBT data. */ + @Override + public void readVariantSaveData( CompoundNBT saveTag ) { + setPathfindingMalus( PathNodeType.WATER, PathNodeType.WALKABLE.getMalus() ); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/WaterSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/WaterSpiderEntity.java index 1d87062..73ff361 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/spider/WaterSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/WaterSpiderEntity.java @@ -33,7 +33,7 @@ public class WaterSpiderEntity extends _SpecialSpiderEntity { bestiaryInfo.color( 0x2D41F4 ).theme( BestiaryInfo.Theme.WATER ) .uniqueTextureWithEyes() .addExperience( 1 ).drownImmune().fluidPushImmune() - .addToAttribute( Attributes.ATTACK_DAMAGE, 1.0 ); + .addToAttribute( Attributes.ATTACK_DAMAGE, 1.0 ).addToRangedDamage( 1.0 ); } @SpecialMob.LanguageProvider diff --git a/src/main/resources/assets/specialmobs/textures/entity/cave_spider/desert.png b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/desert.png new file mode 100644 index 0000000000000000000000000000000000000000..68e388ffb92e1907995369ba713cb6ca2069b08e GIT binary patch literal 974 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3-p)I`?e@QjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`V*`9bT;qMrDiXcAi^4q2wdS@aub!BHWMxx?i%DjXU8J+Isk+RXi3Qz7 z;YU_Ag&by34q&KA@Dej%>@Ex|i*?Toww>3S(o+<+bMsCjqW=)&Jo%C!zu^CP0mE6zG6A4c&H|6fVg?3oVGw3ym^DX&fr06~r;B5VhxgPe zx64)=@VMmm80sxqxYRS#>+}ElmETUBYn}IGA(yyvrknCj?RPoP=9KUH`SbHl^MmV- z#ozZmT>4XQM$*&+zmG}Fh}DWR+-$qbof>C%?m>g>l(0ci_fOENkbiqupqPq4XQ zR==pcdZTH>#M{*u&bB#h77Bay+UvE{eyNOtjitIGA#2`AH+pS0(v74(wH2aeTt`qo;Nq zTmOtfyk_I8eL2Y+r~JB8yi}&B&hw?}^EWrlwyb6p%rKsCX+wF)Q8SiWsbhBy_;#LG zT;aoUbNjx|-uk=8Yp=h3{5nq7ea&=@t-tre+G=B z$SZZ>g(oZA+jm_!vGRX4nlZE9C2;4NkbrvA tj`@NsrbTLIpI^ml!esye literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/specialmobs/textures/entity/cave_spider/desert_eyes.png b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/desert_eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..b608ae35648dd05e2d582606db4a554fe43cbf6b GIT binary patch literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3HE3&8=$zQjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`ISV`@iy0WWg+Z8+Vb&Z8pkSJ(i(^QJ^V^FXIU53aS{_z2--zPYd!Qkf z_lBu2A@?;yB@54uj$YTAKk>zlCYDM-EesAur?sThXP%#^_w4sR&$xB-PbDARUK{-A xc6|Nbn3?mY+kIb@XHY3O`|q(5O0fcQJzf1=);T3K0RT0ZL!P)Px#1ZP1_K>z@;j|==^1poj5Cs0gOMI{#*EFK^>BPGdf0^gz=B^DSxC@VrNE>ANw z@6A@1O)VS`5C8xGN!b*~0000DbW%=J|NsC0|NsC0|NsC006pj;h5!Hn32;bRa{vGf z6951U69E94oEQKA0*6UNK~zXfb(M)?gD?z3$*}}j|NncZC%e$JtAP??OFp67!?JB< z_T0&(Kp^_TXg7pn+3b(JD?T?nWZSfbAr>(pN&v~Y85Llp9u+cyEOwQ?-1 zQ-2BZ&pC#>DXZLu;9}6i$#|XmOXL>(w3RG!n;Hn@E!7Wd6MWWS?!fm6K-WT$W2l>T zu}=LZo4|n&$?azwLtJ848A^m%l;f!cIs_U*o<@urvJj}iC^;B?BB^DRI+g!E+rD?* z$aregiO4}>E%q%2$QgYO_GEl0fL-U&-7MqLt^~#_T<4H8W!pJ>n@2x7cQ*n8J6Z`Q zLLG1}yi+Cqeb&9V-uBActX9e_v6dhpj7-q(YuTIpIs@HW+oL)W;YvWGRUj1&0wR@J zDYKn*^ka^rk9xK#0=(PgErOwOi6EfYX|wu!o~<8}-p=zk_M*#gT`*c45JV6k1NAN| zfd+vr=mBBEkgBh;5C}JUWy#$&QVmn}Rn`a)y!%H4dQ8<<(Tp(bB#*hi8Q_V-^%ynw z^(t#R60^t#TprbA)m|g+(G`@o8_XV58u7#|q6l^z1L0#u079f!S|SoT(YgL3JwECrz3~=1v!CM}WKhC}{59ug#5C8xG07*qo IM6N<$g5aWM^#A|> literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/specialmobs/textures/entity/cave_spider/fire_eyes.png b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/fire_eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..03803b4517d519df7d581b0d76465e4360e81ebd GIT binary patch literal 272 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3-p)I`?e@QjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1``2&1HTup2_p@WjftI+EgLqS7GoWnhikQNI@wZ_vzA~u^@yeItJ<50@nrzP*JXELD-=m-W+S3j3^ HP6Px#1ZP1_K>z@;j|==^1poj5JWxzjMMhd!V0ChFiibp5Sc9FMkE*Do!M|mAcXo}8 zh@haEG;d@mOG#W@-ASL7t*z|z^%f;1%GTDc#l-*s0Pin?5^GIJ7-fB44KI!VAI1bxn=$GDSDJuqQ| zAZOq_H29kXh&D}45lD6q&c8JCDsW(Pt8SWamX^>+=20tieJ#B<4xa>=el zl)(|j+5ZUCgWrr5;V z$*uRqwxQkfB7&-JwrKQ?5JBwTz`D)b zHb);?w9`^olycj&T$k%)nyU)6+*J;&+ng)nyJZNxK+BzPFi-cr(m^U<*zc3?({btt z)@?3Qhhlo@0?ax?uKR8UlCs~6?R$kl**)Z?1ozrT4@A!e0gj!=$6o6G!DNph?I9UX z1bv&g);_(7!o$$U(px(d%xzvF;2f#HvcC~<@Y=dI1kilt&-OpW-+2WBws*T}MLhzR}U%cyc%f=4455GJbw4l4B$WP}?StNO>0%AKC8&L@Kc$ zw9o~p6_J)j&n=Z?8JLqL&q_2I^eO8@7W~OTV(QVey&;H5310)O&{dQU_jOP5hG1RQ zFTvISi1)o<)SO9EQSuJV$r6D`_kSX1a0Iuz1k|>QWPL}VTyBxppJyk~5H&F0?D6;q X_-sxAj6?W900000NkvXXu0mjfi>aeW literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/specialmobs/textures/entity/cave_spider/pale_eyes.png b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/pale_eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..7e0452603c7c4567674618a073233fd676fb556f GIT binary patch literal 225 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3-p)I`?e@QjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`c>;VwTxXi(r)YTJ^_&kB;NP&M8>Frz$S?T+|NjgQS}VT*`J4qFk;M!Q z+`=Ht$S`Y;1W?e=)5S5w!~1QYAs@&}$AACRwF4JlWPJP}Az#zE+VPDLmxe}->W5{& zEQ>xc9-CbjG9hbYs=AiIQL*eNJ|PS)(@iY|-YjfrVq$&InkIQNTKuxO6AQ?h44$rj JF6*2UngH$-Llgi2 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/specialmobs/textures/entity/cave_spider/water.png b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/water.png new file mode 100644 index 0000000000000000000000000000000000000000..712c711f59725287ec923fa94c60b299eee0f375 GIT binary patch literal 887 zcmV--1Bm>IP)Px#1ZP1_K>z@;j|==^1poj5IZ#YgMY-77#^U29N=FkkG(mK6Op}Z#TUaPoSTA8+ zY>|;UaBxL{e^Z>CdjL{B051OkstX$%9xpEd007+z(i{K)026dlPE-H?|NsC0|NsC0 z|NsC0|NsC00J44D2mk;832;bRa{vGi!2kdb!2!6DYwZ940;owuK~zXfb(IT)f*=rt zd1N7IdHw$%d*2M$omQI)E<3}%r0}$am~sp$^Gna?WxD#BEtC46A(YQa`=q^pcp#7{t-Cr{?lWBd!qr^ zxKjdM4g~Cp2>Y68KGt6{69LdUS13r>P>X;C1@9p3q~~M(B@m?6AOJ0^p|%G*0~57F zpO5vI%)-k9Sz$5M$^js&a9`9Wc+bIHTjiQXE*FQ|1ONeP@LHdb^_S$fG%jH+L%o-= z3w5MOq*$G8DJ4sz=_-P|RMKR}jhqPNC%F!U(!DCF3X<6TzAP()B5o)hNGV5xLPwUF z!F#J9ZXb*vIIwU%Ru{{7t)2(~_FJ`L1DVLSwe{W}>tof}I)%2!%Towcnk~sePp3nb z|4S>&dSCBL(cG+F%qN1<5FijZKyP2t5;ydUfMJVE&rbL7EeEYR2O^NCn5ERVtyDdW=)OrqL&k>i+%J%c~6Sms6z+~$>)lkk3tR+&$QixFm-HEq`I?IWG z9wE>Ym0uCOe-mdp<{*~FXJ2D*@b06r^i_@oVOHX2^1uiIJY(srsORWjb%Z4H)y~mV zrO2Ow*{JTZV!H%frZLnhn9a(6q@Tg4?yR7NLA@D1h7)KIjNCyScsiN&Js2pRNYE7G zN*}|)0~zzt>8OOl!u!tP1JK>^Iibq8EcMNiwDF^XpfucE=KMmCGlqt%Zj3V5+{j*w zf&W6llK(0=Wo)c2<+e&VX~*$0FbB~e3s#c8t)E9F4C44Z2mj>x`~}l8PS<{`)OG*> N002ovPDHLkV1lv7mTdq4 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/specialmobs/textures/entity/cave_spider/water_eyes.png b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/water_eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..f83cac73c6b04cc838b61cafa921be4122a77f1e GIT binary patch literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3HE3&8=$zQjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`ISV`@iy0XB4uUY_j)~cCfr5peE{-7?&TlUq+L=syYy4R_w39MH{1VAy8K{W()(}z zPYP#US$Tbh>YVL2OswwipL#TNUjMg^Te}2oGyk2r%?Q$K;2Oo~+n{p9e#`E;Aik%o KpUXO@geCw)`%`cL literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/specialmobs/textures/entity/cave_spider/web_eyes.png b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/web_eyes.png index d4561a2aad2ca2747439b469626413de12d2c3a6..e53b78a85b9ee121e7ef9bdbac49cd854b30d6fb 100644 GIT binary patch delta 39 ucmeyv^oMDJr;L%gxr3*hnue;poOF9|LR(P0yr3up1H-*(l?5A9#TfzY8w+g! delta 39 vcmeyv^oMDJr%Y3O`-GX($|_3p^RnB66WW5}n{#kRdr*3tptw8(14FRd(`Oq~#Tfw`tPMs0