Jump to content

Custom Fluid Help


broede

Recommended Posts

Greetings all, I've been modding with Forge for about a month or two now and I have been writing plugins for a few months longer than that.  I've recently decided to add a customized fluid called "sludge" to my mod-in-progress.  Now, I think I've registered everything okay.  The texture works, it loads, I can pour the fluid into the world, it spreads and what-not just fine.  I even have a custom MaterialLiquid for it.

 

My problem -- how do I get it to behave like water when it comes to interacting with entities?  i.e. pushback on flow, drowning, enable swimming, etc?  It currently just behaves like a gas.  Where do I begin searching for information about adding liquid behaviors?

 

Thank you in advance and happy coding to you all!

Link to comment
Share on other sites

You would have to make your fluid use Material.water

 

All entities check for Material.water for water-behavior (drowning, swimming, etc.)

 

(pro-tip: you can't drown in lava)

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

Won't that cause a conflict when doing bucket fill events?  (i.e. clicking a sludge block with Material.Water turn it into a bucket of water)  And would I still be able to add custom effects from custom fluids a-la the burning effect from lava?

 

Also, the particle effects mimic the water effects (like the blue splashes and the bubbles).  I'd like to customize these as well as the sounds made.  So I don't think using the Material.water material is a good idea.  I've been combing over the Java files but I'm just not certain where the specific fluid events for water and lava are handled.  *sighs*

Link to comment
Share on other sites

I know I've found what causes drowning and swimming and it's "get the block I'm in.  Is its material water?  If yes, do X, if not do Y."

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

Can you point me in the direction of a specific class, perhaps?

 

Also, I've noticed it doesn't animate.  I'm not sure how to do this, either.  I've been googling for results but the only thing I can seem to come up with is the basic fluid tutorial (which I've already been able to do easily, which is nothing more than creating the fluid and making it exist/render) or threads talking about how to make buckets pick up custom liquids.  I, for the life of me, just cannot seem to find any help regarding more advanced information about fluids.

Link to comment
Share on other sites

EntityLivingBase

 

onEntityUpdate() and handleWaterMovement()

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

My guess is that you are going to have to look into @Overriding some of the methods in BlockFluid, I haven't done much with liquids, other than think about adding them, but a quick look over the code shows that:

 

public void randomDisplayTick(World par1World, int par2, int par3, int par4, Random par5Random) handles you particles, you will find the part that handles Material.water, change that for your block

 

There is also a getBlockColor(), and a colorMultiplier(), looking at these might help with color based on Biomes...

 

But again these are guesses and I haven't actually implemented anything with these yet.

Link to comment
Share on other sites

My guess is that you are going to have to look into @Overriding some of the methods in BlockFluid, I haven't done much with liquids, other than think about adding them, but a quick look over the code shows that:

 

public void randomDisplayTick(World par1World, int par2, int par3, int par4, Random par5Random) handles you particles, you will find the part that handles Material.water, change that for your block

 

There is also a getBlockColor(), and a colorMultiplier(), looking at these might help with color based on Biomes...

 

But again these are guesses and I haven't actually implemented anything with these yet.

 

All the tutorials I've seen used BlockFluidClassic instead of BlockFluid.  Is this going to be an issue if I switched over to extending BlockFluid instead?  Please note that BlockFluidClassic does not have those methods.  Either way, I'm going to look into messing with it this way and see what comes up.

Link to comment
Share on other sites

Okay, if I use BlockFluid instead of BlockFluidClassic, I have to set up a separate block for still (BlockStationary) and flowing (BlockFlowing).  This seems to completely remove the use of FluidRegistry now, and I'm not sure of the ramifications of that in the longterm.

 

Does anyone have intimate knowledge of IFluid, BlockFluidBase or BlockFluidClassic, or setting up customize fluids in-depth?  The more I tinker with this, the more I break what I already had working (which, at the very least, was a flowing, texturized liquid, albeit lacking animation and interactivity).

Link to comment
Share on other sites

the methods I mentioned also exist in BlockFluidClassic as BlockFluidClassic extends BlockBluidBase, which extends Block (where these methods come from), so try Overriding them and see what happens ;)

 

Okay, I was able to start making some custom effects, however I still cannot figure out how to change the color of the viewport when inside the new fluid to green in the same way that lava turns it to red or water to blue.  If I could just locate where all the actual lava code exists... I also still can't quite figure out how to add the force of flow and swimming effects.  I appreciate all the help so far but the big stuff I really wanna know is still eluding me!

 

Edit:  Just to be clear, overriding those methods did nothing.

Link to comment
Share on other sites

Hi

 

Did you look in EntityRenderer.updateFogColor for the viewport colour change?

 

-TGG

 

Okay, I did locate this but I know very little about the rendering engine.  I'm going to spend some time figuring it out best I can.  Am I going to have no choice but to edit the EntityRenderer.java file, or will I still be able to extend the class?

 

While I look at that, where can I go about dealing with the entity resistance in a fluid, such as being able to swim, being pushed by the current, that sort of thing?  Are these things dealt with at the fluid level, or at the entity level?

Link to comment
Share on other sites

Hi

 

This is the bit you're interested in from EntityRenderer,  I think:

 

        int i = ActiveRenderInfo.getBlockIdAtEntityViewpoint(this.mc.theWorld, entitylivingbase, par1);
        float f8;

        if (this.cloudFog)
        {
            Vec3 vec33 = worldclient.getCloudColour(par1);
            this.fogColorRed = (float)vec33.xCoord;
            this.fogColorGreen = (float)vec33.yCoord;
            this.fogColorBlue = (float)vec33.zCoord;
        }
        else if (i != 0 && Block.blocksList[i].blockMaterial == Material.water)
        {
            f8 = (float)EnchantmentHelper.getRespiration(entitylivingbase) * 0.2F;
            this.fogColorRed = 0.02F + f8;
            this.fogColorGreen = 0.02F + f8;
            this.fogColorBlue = 0.2F + f8;
        }
        else if (i != 0 && Block.blocksList[i].blockMaterial == Material.lava)
        {
            this.fogColorRed = 0.6F;
            this.fogColorGreen = 0.1F;
            this.fogColorBlue = 0.0F;
        }

 

I think the changing the viewport colour might be a challenge, not sure how best to do that myself

 

The effects of liquids seem to all be handled in the various Entity update methods

 

Air supply is in EntityLivingBase.onEntityUpdate

Entity.handleWaterMovement appears to take care of water current effects

Swimming upwards in liquid is handled by this bit in EntityLivingBase.onLivingUpdate

        if (this.isJumping)
        {
            if (!this.isInWater() && !this.handleLavaMovement())
            {
                if (this.onGround && this.jumpTicks == 0)
                {
                    this.jump();
                    this.jumpTicks = 10;
                }
            }
            else
            {
                this.motionY += 0.03999999910593033D;
            }

 

-TGG

 

 

 

 

Link to comment
Share on other sites

So I have an interesting problem.  I have a strong feeling that the pushback effects of water/lava have to do with the following:

 

BlockFluid.getFlowDirection()

BlockFluid.getFlowVector()

BlockFluid.velocityToAddToEntity()

 

So I have my own class, DCBlockFluid, that extends BlockFluidClassic.  I've pretty much copied all the code over from BlockFluid into DCBlockFluid and tweaked it to deal with my own fluids.  I have particular interest in getFlowDirection() at the moment, so here's the code.

 

    /**
     * the sin and cos of this number determine the surface gradient of the flowing block.
     */
    public static double getFlowDirection(IBlockAccess par0IBlockAccess, int par1, int par2, int par3, Material par4Material)
    {
        Vec3 vec3 = null;

        if (par4Material == DCMod.SLUDGE)
        {
            vec3 = DCMod.blockSludge.getFlowVector(par0IBlockAccess, par1, par2, par3);
        }

        else if (par4Material == DCMod.WASTE)
        {
            vec3 = DCMod.blockWaste.getFlowVector(par0IBlockAccess, par1, par2, par3);
        }
        
        else
        	return 0.0F;

        return vec3.xCoord == 0.0D && vec3.zCoord == 0.0D ? -1000.0D : Math.atan2(vec3.zCoord, vec3.xCoord) - (Math.PI / 2D);
    }

 

If I try to use the @Override annotation, it tells me I have to have a supertype in Block, BlockFluidClassic, BlockFluidBase or IFluidBlock.  So, I don't attempt to use that annotation.  However, if I run the game as is, I get a NullPointerException:

 

at net.minecraft.block.BlockFluid.getFlowDirection(BlockFluid.java:569)

 

Which looks like this, and the line in question is the return line...

 

    public static double getFlowDirection(IBlockAccess par0IBlockAccess, int par1, int par2, int par3, Material par4Material)
    {
        Vec3 vec3 = null;

        if (par4Material == Material.water)
        {
            vec3 = Block.waterMoving.getFlowVector(par0IBlockAccess, par1, par2, par3);
        }

        if (par4Material == Material.lava)
        {
            vec3 = Block.lavaMoving.getFlowVector(par0IBlockAccess, par1, par2, par3);
        }

        return vec3.xCoord == 0.0D && vec3.zCoord == 0.0D ? -1000.0D : Math.atan2(vec3.zCoord, vec3.xCoord) - (Math.PI / 2D);
    }

 

So that means the getFlowDirection() code in DCBlockFluid is not being called -- BlockFluid is.  But clearly, the blocks in question are DCBlockFluids.

 

// New fluids.
public static Fluid fluidSludge;
public static DCBlockFluid blockSludge;

public static Fluid fluidWaste;
public static DCBlockFluid blockWaste;

public void BuildFluids() {		
	// Sludge.  The result of mixing Waste with Water.  Non-toxic.
	fluidSludge = new DCFluid("Sludge")
	 .setViscosity(2000)
	 .setDensity(
	 .setUnlocalizedName("fluidSludge")
	 .setBlockID(blockRegistry.newID());
	FluidRegistry.registerFluid(fluidSludge);
	blockSludge = (DCBlockFluid) new DCBlockFluid(fluidSludge.getBlockID(), fluidSludge, SLUDGE)
	 .setHardness(100.0F)
	 .setLightOpacity(3)
	 .setUnlocalizedName("blockSludge")
	 .setTextureName("blockSludge");
	LanguageRegistry.addName(blockSludge, "Sludge");
	GameRegistry.registerBlock(blockSludge, "blockSludge");

	// Waste.  Toxic to the living.  Only found in the Bayou dimension.
	fluidWaste = new DCFluid("Waste")
	 .setViscosity(7500)
	 .setDensity(3)
	 .setLuminosity(15)
	 .setUnlocalizedName("fluidWaste")
	 .setBlockID(blockRegistry.newID());
	FluidRegistry.registerFluid(fluidWaste);
	blockWaste = (DCBlockFluid) new DCBlockFluid(fluidWaste.getBlockID(), fluidWaste, WASTE)
	 .setHardness(100.0F)
	 .setLightValue(1.0F)
	 .setUnlocalizedName("blockWaste")
	 .setTextureName("blockWaste");
	LanguageRegistry.addName(blockWaste, "Waste");
	GameRegistry.registerBlock(blockWaste, "blockWaste");

}

 

I'm scratching my head.  What am I missing here?  Why is the legacy method BlockFluid.getFlowDirection() being called instead?  Do I have to hardcode core Minecraft classes to get things to work?  I've been trying like mad to avoid doing that for the last two months and have done a bang up job -- until now.  I'm seriously scratching my head over this.

 

If anyone has ANY insight, I would greatly appreciate it.

 

By the by, TGG, thank you for pointing out the information about the render field.  Unfortunately, the only way I could see to get my new fluids to update fog effects was to edit the EntityRender.java class.  :(

 

I dug further and found that BlockFluid.getFlowDirection() is being called from line 3872 of net.minecraft.client.renderer.RenderBlocks:

 

                float f10 = (float)BlockFluid.getFlowDirection(this.blockAccess, par2, par3, par4, material);

 

So despite the fact that I'm using a DCBlockFluid, it's still calling BlockFluid.getFlowDirection() from here.  Which makes sense, but I'm not sure how to modify that without making direct edits, which kind of defeats the purpose in creating whole new classes in the first place, right?

 

Either I:

 

  • Add a supertype into BlockFluidClassic (not sure that'll change anything at this level)
  • Instead extend BlockFluid (which actually won't change a thing because this same problem happened when I was using BlockFluid/BlockFlowing before)
  • Edit Minecraft classes

 

Am I missing something?  I tell ya, if I ever get this working, I'm making a full-blown tutorial on this stuff.  This has got to be the least covered topic on the internet I've seen for modding, and I can see why.

Link to comment
Share on other sites

Hi

 

If I try to use the @Override annotation, it tells me I have to have a supertype in Block, BlockFluidClassic, BlockFluidBase or IFluidBlock. 

 

That means you've gotten the "signature" wrong on your method so it will never be called - i.e. the name is wrong or the parameters are different.  In this case, only BlockFluid has a Material parameter, but BlockFluidClassic derives from BlockFluidBase not BlockFluid.

eg

BlockFLuid:

    public static double getFlowDirection(IBlockAccess par0IBlockAccess, int par1, int par2, int par3, Material par4Material)

 

BlockFluidBase:

    public static double getFlowDirection(IBlockAccess world, int x, int y, int z)

 

If you derive your DCBlockFluid from BlockFluidClassic, you don't need the code to pass the material in because your DCBlockFluid already knows it.

 

BlockFluid is vanilla, BlockFluidClassic is Forge.  Use BlockFluidClassic.  The renderer will call from RenderBlockFluid.renderWorldBlock (confusing choice of name for that class!), not RenderBlocks.renderBlockFluids.

 

-TGG

 

 

 

Link to comment
Share on other sites

Hi

 

This is the bit you're interested in from EntityRenderer,  I think:

 

        int i = ActiveRenderInfo.getBlockIdAtEntityViewpoint(this.mc.theWorld, entitylivingbase, par1);
        float f8;

        if (this.cloudFog)
        {
            Vec3 vec33 = worldclient.getCloudColour(par1);
            this.fogColorRed = (float)vec33.xCoord;
            this.fogColorGreen = (float)vec33.yCoord;
            this.fogColorBlue = (float)vec33.zCoord;
        }
        else if (i != 0 && Block.blocksList[i].blockMaterial == Material.water)
        {
            f8 = (float)EnchantmentHelper.getRespiration(entitylivingbase) * 0.2F;
            this.fogColorRed = 0.02F + f8;
            this.fogColorGreen = 0.02F + f8;
            this.fogColorBlue = 0.2F + f8;
        }
        else if (i != 0 && Block.blocksList[i].blockMaterial == Material.lava)
        {
            this.fogColorRed = 0.6F;
            this.fogColorGreen = 0.1F;
            this.fogColorBlue = 0.0F;
        }

 

I think the changing the viewport colour might be a challenge, not sure how best to do that myself

 

The effects of liquids seem to all be handled in the various Entity update methods

 

Air supply is in EntityLivingBase.onEntityUpdate

Entity.handleWaterMovement appears to take care of water current effects

Swimming upwards in liquid is handled by this bit in EntityLivingBase.onLivingUpdate

        if (this.isJumping)
        {
            if (!this.isInWater() && !this.handleLavaMovement())
            {
                if (this.onGround && this.jumpTicks == 0)
                {
                    this.jump();
                    this.jumpTicks = 10;
                }
            }
            else
            {
                this.motionY += 0.03999999910593033D;
            }

 

-TGG

 

This information helped quite a bit.  My liquids now have the push back, one of them even does custom damage now.  However, I still have a couple of problems.  The code you suggested I look over for the "swimming" effect... well, the only thing I was able to do by adding checks for my liquids was remove the ability to jump inside of the liquid.  Holding the spacebar does nothing, no jumping or swimming.  There has to be more to it and I will look over EntityLivingBase.moveEntity() in a few minutes.

 

So, I was able to add liquids, add bucket checks, give flow properties and give custom damage.  I even added a few custom particles and figured out custom screen fog.  Thanks to everyone for the help thus far.

 

I still need to figure out how to:  animate fluids, change movement in fluids/allowing swimming, cause the new fluids to react to other fluids in the way that water and lava interact (I'm pretty sure I already know where to look for this, though).  Oh... and flow length.  Almost forgot that one.

 

The major, major problem I have is that some of these effects -- I had to edit core Minecraft code.  *sigh*

Link to comment
Share on other sites

Hi

 

If I try to use the @Override annotation, it tells me I have to have a supertype in Block, BlockFluidClassic, BlockFluidBase or IFluidBlock. 

 

That means you've gotten the "signature" wrong on your method so it will never be called - i.e. the name is wrong or the parameters are different.  In this case, only BlockFluid has a Material parameter, but BlockFluidClassic derives from BlockFluidBase not BlockFluid.

eg

BlockFLuid:

    public static double getFlowDirection(IBlockAccess par0IBlockAccess, int par1, int par2, int par3, Material par4Material)

 

BlockFluidBase:

    public static double getFlowDirection(IBlockAccess world, int x, int y, int z)

 

If you derive your DCBlockFluid from BlockFluidClassic, you don't need the code to pass the material in because your DCBlockFluid already knows it.

 

BlockFluid is vanilla, BlockFluidClassic is Forge.  Use BlockFluidClassic.  The renderer will call from RenderBlockFluid.renderWorldBlock (confusing choice of name for that class!), not RenderBlocks.renderBlockFluids.

 

-TGG

 

Thank you for pointing this out.  However, now that I've looked things over more closely, I'm not sure those methods will matter anyway, since -- as you pointed out -- the current is handled elsewhere anyway.  And if that's the case... what the heck do these methods even do?!?

Link to comment
Share on other sites

A-ha!

 

EntityLivingBase.moveEntity() disables the jumping mechanism while inside of a fluid.  However, you need to tweak EntityLivingBase().moveEntityWithHeading() in order to add the sluggish/swim movements.

 

Also:  updateFallState() is useful for splashing into a fluid to prevent fall damage.

 

I thank everyone for the progress, it's been a big help.  Still working on flow length and animation, however.  Oh, and if anyone is interested, you need to look over GuiIngameForge.java if you want to display the air bar while drowning in a custom fluid type.

Link to comment
Share on other sites

Hi

 

The animation is performed automatically by the vanilla engine since the flowing water is an animated texture.

 

http://www.minecraftforum.net/topic/1881638-animation-in-resource-packs-a-minecraft-16-tutorial/

 

This bit in RenderBlockFluid.renderWorldBlock is a rotation which orients the animated texture in the direction of the flow

 

                float flowDir = (float) BlockFluidBase.getFlowDirection(world, x, y, z);

// ..... snip ....

                    float xFlow = MathHelper.sin(flowDir) * 0.25F;
                    float zFlow = MathHelper.cos(flowDir) * 0.25F;
                    u2 = iconStill.getInterpolatedU(8.0F + (-zFlow - xFlow) * 16.0F);
                    v2 = iconStill.getInterpolatedV(8.0F + (-zFlow + xFlow) * 16.0F);
                    u1 = iconStill.getInterpolatedU(8.0F + (-zFlow + xFlow) * 16.0F);
                    v1 = iconStill.getInterpolatedV(8.0F + (zFlow + xFlow) * 16.0F);
                    u4 = iconStill.getInterpolatedU(8.0F + (zFlow + xFlow) * 16.0F);
                    v4 = iconStill.getInterpolatedV(8.0F + (zFlow - xFlow) * 16.0F);
                    u3 = iconStill.getInterpolatedU(8.0F + (zFlow - xFlow) * 16.0F);
                    v3 = iconStill.getInterpolatedV(8.0F + (-zFlow - xFlow) * 16.0F);

 

What do you mean "flow length"?

 

-TGG

Link to comment
Share on other sites

Hi

 

The animation is performed automatically by the vanilla engine since the flowing water is an animated texture.

 

http://www.minecraftforum.net/topic/1881638-animation-in-resource-packs-a-minecraft-16-tutorial/

 

This bit in RenderBlockFluid.renderWorldBlock is a rotation which orients the animated texture in the direction of the flow

 

                float flowDir = (float) BlockFluidBase.getFlowDirection(world, x, y, z);

// ..... snip ....

                    float xFlow = MathHelper.sin(flowDir) * 0.25F;
                    float zFlow = MathHelper.cos(flowDir) * 0.25F;
                    u2 = iconStill.getInterpolatedU(8.0F + (-zFlow - xFlow) * 16.0F);
                    v2 = iconStill.getInterpolatedV(8.0F + (-zFlow + xFlow) * 16.0F);
                    u1 = iconStill.getInterpolatedU(8.0F + (-zFlow + xFlow) * 16.0F);
                    v1 = iconStill.getInterpolatedV(8.0F + (zFlow + xFlow) * 16.0F);
                    u4 = iconStill.getInterpolatedU(8.0F + (zFlow + xFlow) * 16.0F);
                    v4 = iconStill.getInterpolatedV(8.0F + (zFlow - xFlow) * 16.0F);
                    u3 = iconStill.getInterpolatedU(8.0F + (zFlow - xFlow) * 16.0F);
                    v3 = iconStill.getInterpolatedV(8.0F + (-zFlow - xFlow) * 16.0F);

 

What do you mean "flow length"?

 

-TGG

 

Like how water has a flow length of 8, but lava has a flow length of 3.  The distance of blocks it travels before it stops flowing.

Link to comment
Share on other sites

Hi

 

FLuid flowing outwards takes place in updateTick.

 

in BlockFlowing, the difference between lava and water is set here

    public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random)
    {
        int flowDecayThisBlock = this.getFlowDecay(par1World, par2, par3, par4);   // variable renamed for clarity
        byte flowDecayChangePerBlock = 1;      // b0 before renaming

        if (this.blockMaterial == Material.lava && !par1World.provider.isHellWorld)
        {
            flowDecayChangePerBlock = 2;
        }

 

BlockFluidClassic has analagous code where the flowDecayChangePerBlock is effectively set equal to 1, but of course you can override updateTick to change this to whatever you want.

 

-TGG

Link to comment
Share on other sites

    public static double getFlowDirection(IBlockAccess par0IBlockAccess, int par1, int par2, int par3, Material par4Material)

 

If I try to use the @Override annotation, it tells me I have to have a supertype in Block, BlockFluidClassic, BlockFluidBase or IFluidBlock.

You can't override a static method. Most likely, that method exists and isn't static in the first place.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.