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 1b9eb2b..8984995 100644 --- a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java +++ b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java @@ -8,7 +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 mcp.MethodsReturnNonnullByDefault; +import fathertoast.specialmobs.common.entity.zombifiedpiglin.VampireZombifiedPiglinEntity; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.ItemRenderer; import net.minecraft.client.renderer.entity.SpriteRenderer; @@ -23,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 { @@ -56,8 +53,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 @@ -74,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 da1ea1c..08b575a 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; @@ -13,7 +15,10 @@ import net.minecraft.potion.Effects; import net.minecraft.util.ResourceLocation; import net.minecraftforge.registries.ForgeRegistries; -import java.util.*; +import javax.annotation.Nullable; +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. @@ -22,24 +27,76 @@ 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 * 2.5 ), + HIGHEST( DEFAULT.value * 5.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 ).inWaterBiome().build(), + EnvironmentEntry.builder( DefaultWeight.LOWEST.value ).inHumidBiome().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 ).isRaining().canSeeSky().build(), + EnvironmentEntry.builder( DefaultWeight.HIGH.value ).aboveHalfMoonLight().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 ).inMountainBiome().build(), + EnvironmentEntry.builder( DefaultWeight.HIGHEST.value ).aboveMountainLevel().build(), + 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(), + 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. */ @@ -280,13 +337,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 +408,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; } @@ -435,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; @@ -476,6 +539,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/bestiary/MobFamily.java b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java index c19b792..f34ac3a 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<>(); @@ -44,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 }, @@ -77,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", "Pale", "Water", "Web", "Witch" ); 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, @@ -101,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, @@ -196,31 +196,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; } @@ -238,8 +217,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/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/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..00dd6bb 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,12 @@ 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.IntField; +import fathertoast.specialmobs.common.config.field.EnvironmentListField; 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,9 +53,9 @@ public class FamilyConfig extends Config.AbstractConfig { public final DoubleField familyRandomScaling; - public final DoubleField specialVariantChance; + public final DoubleField.EnvironmentSensitive specialVariantChance; - public final IntField[] specialVariantWeights; + public final DoubleField.EnvironmentSensitiveWeightedList> specialVariantList; General( ToastConfigSpec parent, MobFamily family, double variantChance ) { super( parent, "general", @@ -72,27 +74,49 @@ 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(); 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/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..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; @@ -23,12 +27,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; @@ -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/EntityListField.java b/src/main/java/fathertoast/specialmobs/common/config/field/EntityListField.java index 1604190..ffc7a9c 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; @@ -35,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 ); } @@ -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. */ @@ -203,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. @@ -222,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; } /** @@ -242,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; } /** @@ -252,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..b9cc7e7 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/field/EnvironmentListField.java @@ -0,0 +1,312 @@ +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.*; +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; +import java.util.Locale; + +/** + * 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 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 + */ + // 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_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"; + public static final String ENV_BIOME_CATEGORY = "biome_category"; + public static final String ENV_BIOME = "biome"; + // 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"; + 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"; + 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. + * 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_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 + "\":" ); + 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[]) 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 + 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/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." ); + // 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( " 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; + } + + /** Creates a new field. */ + public EnvironmentListField( String key, EnvironmentList defaultValue, @Nullable 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].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].toLowerCase( Locale.ROOT ) ) { + // 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_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: + 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 ); + case ENV_POSITION: + return new PositionEnvironment( 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 ); + 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 + final String[] environmentNames = { + // Dimension-based + ENV_DIMENSION_PROPERTY, ENV_DIMENSION_TYPE, + // Biome-based + 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 + ENV_DIFFICULTY, ENV_SPECIAL_DIFFICULTY, ENV_WEATHER, ENV_MOON_BRIGHTNESS, ENV_MOON_PHASE, ENV_DAY_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: {}", + 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/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/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/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/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/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/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/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/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/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, '~' ); 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..b73fa1d --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/EnvironmentEntry.java @@ -0,0 +1,319 @@ +package fathertoast.specialmobs.common.config.util; + +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.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.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(); + } + + + // 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 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; + 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 + + /** 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 + + /** 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 ); } + + /** 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 + + /** Check if the position is inside a particular structure. See {@link Structure}. */ + public Builder inStructure( Structure structure ) { return in( new StructureEnvironment( structure, false ) ); } + + /** Check if the position is inside a particular structure. See {@link Structure}. */ + public Builder notInStructure( Structure structure ) { return in( new StructureEnvironment( structure, true ) ); } + + /** Check if diamond/redstone ore can generate at the position. */ + public Builder belowDiamondLevel() { return belowY( 15 ); } // TODO update ore-based logic in 1.18 + + /** Check if diamond/redstone ore can generate at the position. */ + public Builder aboveDiamondLevel() { return aboveY( 15 ); } + + /** 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 ) ); } + + /** 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 ); } + + 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, false ) ); } + + 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/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..1c34862 100644 --- a/src/main/java/fathertoast/specialmobs/common/config/util/WeightedList.java +++ b/src/main/java/fathertoast/specialmobs/common/config/util/WeightedList.java @@ -13,10 +13,8 @@ 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; - /** The weighted entries in this list. */ private final List> ENTRIES; /** The total weight of all entries in this list. */ @@ -25,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<>(); @@ -110,6 +108,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..f960a90 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/ComparisonOperator.java @@ -0,0 +1,76 @@ +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; } + + /** @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; + } + 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..f4e1bb4 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryEnvironment.java @@ -0,0 +1,76 @@ +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, 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..b093904 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/DynamicRegistryGroupEnvironment.java @@ -0,0 +1,85 @@ +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, 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..71ec5c7 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/EnumEnvironment.java @@ -0,0 +1,39 @@ +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, 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..a638d41 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryEnvironment.java @@ -0,0 +1,57 @@ +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, 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..ef9a71d --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/RegistryGroupEnvironment.java @@ -0,0 +1,69 @@ +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, boolean invert ) { + //noinspection ConstantConditions + this( regEntry.getRegistryName(), invert ); + } + + public RegistryGroupEnvironment( ResourceLocation regKey, boolean invert ) { + FIELD = null; + INVERT = invert; + NAMESPACE = regKey.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/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 new file mode 100644 index 0000000..c065716 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeCategoryEnvironment.java @@ -0,0 +1,26 @@ +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 javax.annotation.Nullable; + +public class BiomeCategoryEnvironment extends EnumEnvironment { + + public BiomeCategoryEnvironment( BiomeCategory value, boolean invert ) { super( value, invert ); } + + 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 + 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..9e3c704 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeEnvironment.java @@ -0,0 +1,34 @@ +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, 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..ede1a0f --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeGroupEnvironment.java @@ -0,0 +1,44 @@ +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.ResourceLocation; +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, boolean invert ) { this( biome.getRegistryName(), invert ); } + + public BiomeGroupEnvironment( ResourceLocation regKey, boolean invert ) { super( regKey, 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..f7a5312 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/BiomeTemperatureEnvironment.java @@ -0,0 +1,28 @@ +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.ComparisonOperator; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +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, 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 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..d833d4b --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/RainfallEnvironment.java @@ -0,0 +1,39 @@ +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; + if( COMPARATOR == ComparisonOperator.NOT_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..200dc3d --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/biome/TemperatureEnvironment.java @@ -0,0 +1,50 @@ +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 final float FREEZING_POINT = 0.15F; + + public static String handleTempInput( String line ) { + if( line.equalsIgnoreCase( FREEZING ) ) + return ComparisonOperator.LESS_THAN + " " + FREEZING_POINT; + if( line.equalsIgnoreCase( "!" + FREEZING ) ) + 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 ) ); } + + /** @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 == FREEZING_POINT ) return FREEZING; + if( COMPARATOR == ComparisonOperator.LESS_THAN.invert() && VALUE == FREEZING_POINT ) 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/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/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..50f56e1 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionPropertyEnvironment.java @@ -0,0 +1,49 @@ +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, 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..e3eecb6 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeEnvironment.java @@ -0,0 +1,34 @@ +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, 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..516dd03 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/dimension/DimensionTypeGroupEnvironment.java @@ -0,0 +1,42 @@ +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.ResourceLocation; +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, boolean invert ) { this( dimType.getRegistryName(), invert ); } + + public DimensionTypeGroupEnvironment( ResourceLocation regKey, boolean invert ) { super( regKey, 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/PositionEnvironment.java b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/PositionEnvironment.java new file mode 100644 index 0000000..ca2c7a1 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/PositionEnvironment.java @@ -0,0 +1,45 @@ +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_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() ), + 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, 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 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..e3b312e --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureEnvironment.java @@ -0,0 +1,36 @@ +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, 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..819cc12 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/position/StructureGroupEnvironment.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.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; +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 structure, boolean invert ) { super( structure, invert ); } + + public StructureGroupEnvironment( ResourceLocation regKey, boolean invert ) { super( regKey, 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/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 new file mode 100644 index 0000000..1a9a9b9 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DayTimeEnvironment.java @@ -0,0 +1,43 @@ +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, 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..6efb589 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/DifficultyEnvironment.java @@ -0,0 +1,43 @@ +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. */ + @Override + protected float getMinValue() { return 0.0F; } + + // Don't specify the max value, just in case a mod changes it. + + /** @return The string name of this environment, as it would appear in a config file. */ + @Override + public String name() { return EnvironmentListField.ENV_DIFFICULTY; } + + /** @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..705e71f --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/MoonBrightnessEnvironment.java @@ -0,0 +1,36 @@ +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. */ + @Override + protected float getMinValue() { return 0.0F; } + + /** @return The maximum value that can be given to the value. */ + @Override + protected float getMaxValue() { return 1.0F; } + + /** @return The string name of this environment, as it would appear in a config file. */ + @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..f7388dc --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/MoonPhaseEnvironment.java @@ -0,0 +1,36 @@ +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, 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..32a8dfe --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/SpecialDifficultyEnvironment.java @@ -0,0 +1,42 @@ +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. */ + @Override + protected float getMinValue() { return 0.0F; } + + /** @return The maximum value that can be given to the value. */ + @Override + protected float getMaxValue() { return 1.0F; } + + /** @return The string name of this environment, as it would appear in a config file. */ + @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..8a75358 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/config/util/environment/time/WeatherEnvironment.java @@ -0,0 +1,30 @@ +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, 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_WEATHER; } + + /** @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..cd30d40 --- /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_WORLD_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 ) { 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 0ba3626..1206ab3 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. */ @@ -39,7 +35,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 ); @@ -108,7 +105,7 @@ public final class SpecialMobReplacer { /** @return True if the next mob should be made a special variant. */ private static boolean shouldMakeNextSpecial( MobFamily mobFamily, World world, BlockPos entityPos ) { - return world.random.nextFloat() < mobFamily.config.GENERAL.specialVariantChance.get(); //TODO environment exceptions + return world.random.nextDouble() < mobFamily.config.GENERAL.specialVariantChance.get( world, entityPos ); } /** @return True if a mob should be replaced. */ diff --git a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java index 3752895..169c226 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,8 +82,8 @@ public class SpecialMobs { * + ranged attack AI (spitter) * + puffer * - endermen - * - witches TODO inject ranged attack stats - * - ability to equip held items + * - witches + * - 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/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/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/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 diff --git a/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java b/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java index a63aea2..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; @@ -103,14 +112,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; } @@ -327,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. @@ -344,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/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/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/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/blaze/_SpecialBlazeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/blaze/_SpecialBlazeEntity.java index 0093809..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 ) { } @@ -205,11 +206,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 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/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/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/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 b49ea72..1494442 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. */ @@ -104,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 d4ed789..753bb95 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(); } @@ -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 new file mode 100644 index 0000000..5ab9281 --- /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.SetBlockFlags.DEFAULTS ); + 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.SetBlockFlags.DEFAULTS ); + } + } + + /** + * 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..276ad37 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/SnowCreeperEntity.java @@ -0,0 +1,260 @@ +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.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.entity.EntityType; +import net.minecraft.entity.SpawnReason; +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.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; + +@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 ) + .uniqueTextureWithEyes() + .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 ); + MobHelper.hopOnFluid( this ); + } + + /** Called whenever this entity's block position changes. */ + @Override + protected void onChangedBlock( BlockPos pos ) { + super.onChangedBlock( pos ); + MobHelper.updateFrostWalker( this, pos ); + } + + /** 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. */ + @Override + protected void makeVariantExplosion( float explosionPower ) { + final Explosion.Mode explosionMode = ExplosionHelper.getMode( this ); + final ExplosionHelper explosion = new ExplosionHelper( this, + explosionMode == Explosion.Mode.NONE ? explosionPower : 2.0F, false, false ); + if( !explosion.initializeExplosion() ) return; + explosion.finalizeExplosion(); + + final int radius = (int) Math.floor( explosionPower ); + final BlockPos center = new BlockPos( explosion.getPos() ); + + 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; + } + } + } + + /** 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. */ + @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/creeper/_SpecialCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java index 49d9d37..3df472e 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 ) { } @@ -116,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 new file mode 100644 index 0000000..1918fda --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/enderman/FlameEndermanEntity.java @@ -0,0 +1,138 @@ +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 +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 ---------------- + + 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; + } + } + } + + /** @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/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..ab4cd04 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/enderman/RunicEndermanEntity.java @@ -0,0 +1,76 @@ +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.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; + +@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 ); + loot.addClusterDrop( "common", Blocks.STONE ); + loot.addRareDrop( "rare", Items.END_CRYSTAL ); + } + + @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 ) ); + } + } + + // 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/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/CorporealShiftGhastEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/ghast/CorporealShiftGhastEntity.java index 57c3af1..97cc3a0 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/ghast/CorporealShiftGhastEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/ghast/CorporealShiftGhastEntity.java @@ -4,9 +4,10 @@ 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; @@ -63,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 ); } @@ -72,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 @@ -80,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 ); } @@ -88,20 +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(); } + + @Override + 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(); @@ -110,7 +124,7 @@ public class CorporealShiftGhastEntity extends _SpecialGhastEntity { 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 ); + final IncorporealFireballEntity fireball = new IncorporealFireballEntity( level, this, dX, dY, dZ ); fireball.explosionPower = getVariantExplosionPower( getExplosionPower() ); fireball.setPos( getX() + lookVec.x, @@ -119,15 +133,6 @@ public class CorporealShiftGhastEntity extends _SpecialGhastEntity { 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 ); - } - /** Override to save data to this entity's NBT data. */ @Override public void addVariantSaveData( CompoundNBT saveTag ) { 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/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/entity/silverfish/DesiccatedSilverfishEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/DesiccatedSilverfishEntity.java new file mode 100644 index 0000000..e4ca6ec --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/DesiccatedSilverfishEntity.java @@ -0,0 +1,171 @@ +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; +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.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 { + + //--------------- 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.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", + "", "", "", "", "", "" );//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 ---------------- + + 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() { + if( absorbCount > 0 && spongebob() ) spawnAnim(); + super.aiStep(); + } + + /** 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; } + + /** @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/silverfish/FishingSilverfishEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/FishingSilverfishEntity.java index df88d0c..899c1aa 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() @@ -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/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/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/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/FireSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/FireSpiderEntity.java new file mode 100644 index 0000000..b34031b --- /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.SetBlockFlags.DEFAULTS ); + } + } + 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..73ff361 --- /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 ).addToRangedDamage( 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/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/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 f9bfcd5..7da3693 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; @@ -53,9 +55,10 @@ 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 ); + .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/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/zombie/FishingZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/FishingZombieEntity.java index 42742e4..fee425e 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 ); @@ -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 78df96d..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 ) + 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/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/FishingZombifiedPiglinEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombifiedpiglin/FishingZombifiedPiglinEntity.java index 9befadf..d7ec4d5 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 ); @@ -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/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/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/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); } } 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..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; @@ -61,15 +82,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_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_TEXTURE = "Texture"; + // public static final String TAG_TEXTURE_EYES = "TextureEyes"; + // public static final String TAG_TEXTURE_OVER = "TextureOverlay"; 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/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/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 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 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 0000000..68e388f Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/desert.png differ 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 0000000..b608ae3 Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/desert_eyes.png differ diff --git a/src/main/resources/assets/specialmobs/textures/entity/cave_spider/fire.png b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/fire.png new file mode 100644 index 0000000..f901b3e Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/fire.png differ 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 0000000..03803b4 Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/fire_eyes.png differ diff --git a/src/main/resources/assets/specialmobs/textures/entity/cave_spider/pale.png b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/pale.png new file mode 100644 index 0000000..3534ddf Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/pale.png differ 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 0000000..7e04526 Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/pale_eyes.png differ 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 0000000..712c711 Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/water.png differ 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 0000000..f83cac7 Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/water_eyes.png differ 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 eebc22b..e53b78a 100644 Binary files a/src/main/resources/assets/specialmobs/textures/entity/cave_spider/web_eyes.png and b/src/main/resources/assets/specialmobs/textures/entity/cave_spider/web_eyes.png differ 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 0000000..28b319c Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/creeper/sand.png differ diff --git a/src/main/resources/assets/specialmobs/textures/entity/creeper/snow.png b/src/main/resources/assets/specialmobs/textures/entity/creeper/snow.png new file mode 100644 index 0000000..43054df Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/creeper/snow.png differ diff --git a/src/main/resources/assets/specialmobs/textures/entity/creeper/snow_eyes.png b/src/main/resources/assets/specialmobs/textures/entity/creeper/snow_eyes.png new file mode 100644 index 0000000..0b2f20b Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/creeper/snow_eyes.png differ 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 f40b191..4cf73a1 100644 Binary files a/src/main/resources/assets/specialmobs/textures/entity/creeper/splitting.png and b/src/main/resources/assets/specialmobs/textures/entity/creeper/splitting.png differ diff --git a/src/main/resources/assets/specialmobs/textures/entity/creeper/splitting_eyes.png b/src/main/resources/assets/specialmobs/textures/entity/creeper/splitting_eyes.png index 156890c..554b06e 100644 Binary files a/src/main/resources/assets/specialmobs/textures/entity/creeper/splitting_eyes.png and b/src/main/resources/assets/specialmobs/textures/entity/creeper/splitting_eyes.png differ 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 0000000..2ecc87a Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/enderman/flame.png differ 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 new file mode 100644 index 0000000..da8232e Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/enderman/flame_eyes.png differ 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 0000000..990fbdc Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/enderman/runic.png differ diff --git a/src/main/resources/assets/specialmobs/textures/entity/enderman/runic_eyes.png b/src/main/resources/assets/specialmobs/textures/entity/enderman/runic_eyes.png new file mode 100644 index 0000000..207c504 Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/enderman/runic_eyes.png differ 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 0000000..f407f15 Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/silverfish/desiccated.png differ diff --git a/src/main/resources/assets/specialmobs/textures/entity/silverfish/fishy.png b/src/main/resources/assets/specialmobs/textures/entity/silverfish/puffer.png similarity index 100% rename from src/main/resources/assets/specialmobs/textures/entity/silverfish/fishy.png rename to src/main/resources/assets/specialmobs/textures/entity/silverfish/puffer.png diff --git a/src/main/resources/assets/specialmobs/textures/entity/spider/desert.png b/src/main/resources/assets/specialmobs/textures/entity/spider/desert.png index 1ac3eab..7bdd84f 100644 Binary files a/src/main/resources/assets/specialmobs/textures/entity/spider/desert.png and b/src/main/resources/assets/specialmobs/textures/entity/spider/desert.png differ 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 bcaa2f0..b608ae3 100644 Binary files a/src/main/resources/assets/specialmobs/textures/entity/spider/desert_eyes.png and b/src/main/resources/assets/specialmobs/textures/entity/spider/desert_eyes.png differ 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 0000000..4dc3af2 Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/spider/fire.png differ diff --git a/src/main/resources/assets/specialmobs/textures/entity/spider/fire_eyes.png b/src/main/resources/assets/specialmobs/textures/entity/spider/fire_eyes.png new file mode 100644 index 0000000..03803b4 Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/spider/fire_eyes.png differ 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 0000000..3855edb Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/spider/water.png differ 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 0000000..75f5d41 Binary files /dev/null and b/src/main/resources/assets/specialmobs/textures/entity/spider/water_eyes.png differ 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 485c344..9c5b5c0 100644 Binary files a/src/main/resources/assets/specialmobs/textures/entity/spider/web_eyes.png and b/src/main/resources/assets/specialmobs/textures/entity/spider/web_eyes.png differ 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 23543ed..0e1059d 100644 Binary files a/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/brute.png and b/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/brute.png differ diff --git a/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/brute_overlay.png b/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/brute_overlay.png deleted file mode 100644 index 6ff05b0..0000000 Binary files a/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/brute_overlay.png and /dev/null differ 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 45e00d6..bc07df2 100644 Binary files a/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/hungry.png and b/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/hungry.png differ 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 629cbd6..2ce6183 100644 Binary files a/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/plague.png and b/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/plague.png differ diff --git a/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/vampire.png b/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/vampire.png index 7295587..a32e89b 100644 Binary files a/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/vampire.png and b/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/vampire.png differ 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 d7910eb..0000000 Binary files a/src/main/resources/assets/specialmobs/textures/entity/zombified_piglin/vampire_overlay.png and /dev/null differ