Create water creating and absorbing statues

Joeb7919

Terrarian
Hello all, I am tired of having to build elaborate liquid generating constructions that barely work or are extremely slow and get patched out. So I am trying to make a mod to help those of us who like to build things with actual liquids in and around them, IE underwater/lava/honey/shimmer housing. Or even attach a switch to and create a lava trap for enemies when they enter a hallway.

So I am trying to create statues that generate liquids and absorb liquids. Using example mod I figured out how to create my statues and get them to generate stuff, but I cant' figure out what I am missing to get water from it. Physical items in game come out no problem. But not liquids.

My goal is basically create a statue that is the same as bottomless buckets and/or sponges, but placeable and can simply attach a timer or switch to for activation. Something like the pumps, but not reliant on each other, make one that generates and one that absorbs, but separate things that dont have to be connected. Would be even better if they could somehow be manipulated with a selector to choose what they spawn. Similar to how the wrench can right click and have several options. for wires or cutters.

But suffice it to say it is beyond my meager knowledge and I am hoping someone here could help point me in the right direction and not just say go learn all of C# and .Net.

By the way, here is what I tried for water generation, it generates something since when activated standing next to it gives the pickup sound, but nothing is added to inventory.

if (Main.rand.NextFloat() < .95f)
{
if (Wiring.CheckMech(x, y, 0) && Item.MechSpawn(spawnX, spawnY, LiquidID.Water))
{
int id = LiquidID.Water;
Item.NewItem(entitySource, (int)spawnX, (int)spawnY - -20, 0, 0, id, 1, false, 0, false);
}
}

I have not even started on something that could remove liquids. But figured it would be easier to ask for both and incase someone knows a way to do the selection thing.

Thanks for any help in advance.
 
Water is not an item you can spawn like that.

Tiles have a liquidAmount and liquidType you'd set. Get the tile position and then set those fields for the specific tile to add or remove water.
 
If you would like to add, remove, or change liquid, then you can use Tile.LiquidType and Tile.LiquidAmount. Tile.LiquidAmount is the amount of liquid a tile has. 0 is no liquid, and 255 is completely full. Tile.LiquidType is the type of the liquid the tile contains; you can use LiquidID.
If you want to do things the exact same as the base game (to ensure correctness), then:
  • If a tile's liquid amount is brought to 0 ("emptied"), then then Tile.LiquidType is to be set to 0 (LiquidID.Water)
  • When a tile's liquid type or amount is changed, then WorldGen.TileFrame() is to be called for all tiles who's liquid has changed, along with all tiles touching/around these tiles. Pumps and buckets use WorldGen.SquareTileFrame() for a single tile's liquid being changed. The resetFrame parameter should be true for tile's who have had a change in liquid, and false for those bordering the changed tile.
  • If a tile's liquid is changed on a multiplayer client, like when a bucket/item is used, then NetMessage.sendWater() should be called. This is because WorldGen.TileFrame() calls Liquid.AddWater(), but only when not on a multiplayer client. Liquid.AddWater() calls NetMessage.sendWater(), which will sync a tile's liquid type and amount if on the server.
  • If a tile's liquid is emptied, but not on a client (server or singleplayer), then Liquid.AddWater() should be called. This is because WorldGen.TileFrame() only calls Liquid.AddWater() when the tile has a liquid amount greater than 0.
Here is a function that can be used to fill/empty a block of water similar to how it's done with buckets and pumps:
C#:
static void SetLiquidInBox(int tileX, int tileY, int width, int height, int liquidType, bool makeEmpty)
{
    int x,y;
    Tile tile;
    // fill/empty the tiles
    for(x = tileX; x < tileX + width; x++)
    {
        for(y = tileY; y < tileY + height; y++)
        {
            tile = Main.tile[x, y];
            if(makeEmpty)
            {
                tile.LiquidAmount = 0;
                tile.LiquidType = 0;
            }
            else
            {
                tile.LiquidAmount = 255;
                tile.LiquidType = liquidType;
            }
        }
    }
    // sync and update liquid movement
    for(x = tileX - 1; x <= tileX + width; x++)
    {
        for(y = tileY - 1; y <= tileY + height; y++)
        {
            if(x >= tileX && x < tileX + width && y >= tileY && y < tileY + height)
            {
                WorldGen.TileFrame(x, y, resetFrame: true, noBreak: false);
                if(Main.netMode == NetmodeID.MultiplayerClient)
                    NetMessage.sendWater(x, y);
                else if(makeEmpty)
                    Liquid.AddWater(x, y);
            }
            else
                WorldGen.TileFrame(x, y, resetFrame: false, noBreak: false);
        }
    }
}
WorldGen.PlaceLiquid() may be used to add liquid to a single tile. I would not recommend using this for large amounts of tiles since it calls WorldGen.SquareTileFrame() every time.
Terraria.Wiring.XferWater() is where pumps are handled
Terraria.Player.ItemCheck_UseBuckets() is where bucket liquid placing is handled
 
Last edited:
If you would like to add, remove, or change liquid, then you can use Tile.LiquidType and Tile.LiquidAmount. Tile.LiquidAmount is the amount of liquid a tile has. 0 is no liquid, and 255 is completely full. Tile.LiquidType is the type of the liquid the tile contains; you can use LiquidID.
If you want to do things the exact same as the base game (to ensure correctness), then:
  • If a tile's liquid amount is brought to 0 ("emptied"), then then Tile.LiquidType is to be set to 0 (LiquidID.Water)
  • When a tile's liquid type or amount is changed, then WorldGen.TileFrame() is to be called for all tiles who's liquid has changed, along with all tiles touching/around these tiles. Pumps and buckets use WorldGen.SquareTileFrame() for a single tile's liquid being changed. The resetFrame parameter should be true for tile's who have had a change in liquid, and false for those bordering the changed tile.
  • If a tile's liquid is changed on a multiplayer client, like when a bucket/item is used, then NetMessage.sendWater() should be called. This is because WorldGen.TileFrame() calls Liquid.AddWater(), but only when not on a multiplayer client. Liquid.AddWater() calls NetMessage.sendWater(), which will sync a tile's liquid type and amount if on the server.
  • If a tile's liquid is emptied, but not on a client (server or singleplayer), then Liquid.AddWater() should be called. This is because WorldGen.TileFrame() only calls Liquid.AddWater() when the tile has a liquid amount greater than 0.
Here is a function that can be used to fill/empty a block of water similar to how it's done with buckets and pumps:
C#:
static void SetLiquidInBox(int tileX, int tileY, int width, int height, int liquidType, bool makeEmpty)
{
    int x,y;
    Tile tile;
    // fill/empty the tiles
    for(x = tileX; x < tileX + width; x++)
    {
        for(y = tileY; y < tileY + height; y++)
        {
            tile = Main.tile[x, y];
            if(makeEmpty)
            {
                tile.LiquidAmount = 0;
                tile.LiquidType = 0;
            }
            else
            {
                tile.LiquidAmount = 255;
                tile.LiquidType = liquidType;
            }
        }
    }
    // sync and update liquid movement
    for(x = tileX - 1; x <= tileX + width; x++)
    {
        for(y = tileY - 1; y <= tileY + height; y++)
        {
            if(x >= tileX && x < tileX + width && y >= tileY && y < tileY + height)
            {
                WorldGen.TileFrame(x, y, resetFrame: true, noBreak: false);
                if(Main.netMode == NetmodeID.MultiplayerClient)
                    NetMessage.sendWater(x, y);
                else if(makeEmpty)
                    Liquid.AddWater(x, y);
            }
            else
                WorldGen.TileFrame(x, y, resetFrame: false, noBreak: false);
        }
    }
}
WorldGen.PlaceLiquid() may be used to add liquid to a single tile. I would not recommend using this for large amounts of tiles since it calls WorldGen.SquareTileFrame() every time.
Terraria.Wiring.XferWater() is where pumps are handled
Terraria.Player.ItemCheck_UseBuckets() is where bucket liquid placing is handled

I appreciate the help, but after messing around with this for a few days I clearly do not have the skill and knowledge needed to create this item. Everytime I thought I fixed one thing another was broken and so many things seem to still be missing. With context I do not fully understand since I do not know C. And its not something I could learn easily. I have a horrible memory for detailed specific things and it is super specific where even upper and lower case makes something different. Plus a lot of the circular referencing just makes no sense to me.
 
Back
Top Bottom