001package net.minecraft.block;
002
003import java.util.Random;
004import net.minecraft.block.material.Material;
005import net.minecraft.world.IBlockAccess;
006import net.minecraft.world.World;
007
008public class BlockFlowing extends BlockFluid
009{
010    /**
011     * Number of horizontally adjacent liquid source blocks. Diagonal doesn't count. Only source blocks of the same
012     * liquid as the block using the field are counted.
013     */
014    int numAdjacentSources = 0;
015
016    /**
017     * Indicates whether the flow direction is optimal. Each array index corresponds to one of the four cardinal
018     * directions.
019     */
020    boolean[] isOptimalFlowDirection = new boolean[4];
021
022    /**
023     * The estimated cost to flow in a given direction from the current point. Each array index corresponds to one of
024     * the four cardinal directions.
025     */
026    int[] flowCost = new int[4];
027
028    protected BlockFlowing(int par1, Material par2Material)
029    {
030        super(par1, par2Material);
031    }
032
033    /**
034     * Updates the flow for the BlockFlowing object.
035     */
036    private void updateFlow(World par1World, int par2, int par3, int par4)
037    {
038        int l = par1World.getBlockMetadata(par2, par3, par4);
039        par1World.setBlock(par2, par3, par4, this.blockID + 1, l, 2);
040    }
041
042    public boolean getBlocksMovement(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
043    {
044        return this.blockMaterial != Material.lava;
045    }
046
047    /**
048     * Ticks the block if it's been scheduled
049     */
050    public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random)
051    {
052        int l = this.getFlowDecay(par1World, par2, par3, par4);
053        byte b0 = 1;
054
055        if (this.blockMaterial == Material.lava && !par1World.provider.isHellWorld)
056        {
057            b0 = 2;
058        }
059
060        boolean flag = true;
061        int i1;
062
063        if (l > 0)
064        {
065            byte b1 = -100;
066            this.numAdjacentSources = 0;
067            int j1 = this.getSmallestFlowDecay(par1World, par2 - 1, par3, par4, b1);
068            j1 = this.getSmallestFlowDecay(par1World, par2 + 1, par3, par4, j1);
069            j1 = this.getSmallestFlowDecay(par1World, par2, par3, par4 - 1, j1);
070            j1 = this.getSmallestFlowDecay(par1World, par2, par3, par4 + 1, j1);
071            i1 = j1 + b0;
072
073            if (i1 >= 8 || j1 < 0)
074            {
075                i1 = -1;
076            }
077
078            if (this.getFlowDecay(par1World, par2, par3 + 1, par4) >= 0)
079            {
080                int k1 = this.getFlowDecay(par1World, par2, par3 + 1, par4);
081
082                if (k1 >= 8)
083                {
084                    i1 = k1;
085                }
086                else
087                {
088                    i1 = k1 + 8;
089                }
090            }
091
092            if (this.numAdjacentSources >= 2 && this.blockMaterial == Material.water)
093            {
094                if (par1World.getBlockMaterial(par2, par3 - 1, par4).isSolid())
095                {
096                    i1 = 0;
097                }
098                else if (par1World.getBlockMaterial(par2, par3 - 1, par4) == this.blockMaterial && par1World.getBlockMetadata(par2, par3 - 1, par4) == 0)
099                {
100                    i1 = 0;
101                }
102            }
103
104            if (this.blockMaterial == Material.lava && l < 8 && i1 < 8 && i1 > l && par5Random.nextInt(4) != 0)
105            {
106                i1 = l;
107                flag = false;
108            }
109
110            if (i1 == l)
111            {
112                if (flag)
113                {
114                    this.updateFlow(par1World, par2, par3, par4);
115                }
116            }
117            else
118            {
119                l = i1;
120
121                if (i1 < 0)
122                {
123                    par1World.setBlockToAir(par2, par3, par4);
124                }
125                else
126                {
127                    par1World.setBlockMetadataWithNotify(par2, par3, par4, i1, 2);
128                    par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, this.tickRate(par1World));
129                    par1World.notifyBlocksOfNeighborChange(par2, par3, par4, this.blockID);
130                }
131            }
132        }
133        else
134        {
135            this.updateFlow(par1World, par2, par3, par4);
136        }
137
138        if (this.liquidCanDisplaceBlock(par1World, par2, par3 - 1, par4))
139        {
140            if (this.blockMaterial == Material.lava && par1World.getBlockMaterial(par2, par3 - 1, par4) == Material.water)
141            {
142                par1World.setBlock(par2, par3 - 1, par4, Block.stone.blockID);
143                this.triggerLavaMixEffects(par1World, par2, par3 - 1, par4);
144                return;
145            }
146
147            if (l >= 8)
148            {
149                this.flowIntoBlock(par1World, par2, par3 - 1, par4, l);
150            }
151            else
152            {
153                this.flowIntoBlock(par1World, par2, par3 - 1, par4, l + 8);
154            }
155        }
156        else if (l >= 0 && (l == 0 || this.blockBlocksFlow(par1World, par2, par3 - 1, par4)))
157        {
158            boolean[] aboolean = this.getOptimalFlowDirections(par1World, par2, par3, par4);
159            i1 = l + b0;
160
161            if (l >= 8)
162            {
163                i1 = 1;
164            }
165
166            if (i1 >= 8)
167            {
168                return;
169            }
170
171            if (aboolean[0])
172            {
173                this.flowIntoBlock(par1World, par2 - 1, par3, par4, i1);
174            }
175
176            if (aboolean[1])
177            {
178                this.flowIntoBlock(par1World, par2 + 1, par3, par4, i1);
179            }
180
181            if (aboolean[2])
182            {
183                this.flowIntoBlock(par1World, par2, par3, par4 - 1, i1);
184            }
185
186            if (aboolean[3])
187            {
188                this.flowIntoBlock(par1World, par2, par3, par4 + 1, i1);
189            }
190        }
191    }
192
193    /**
194     * flowIntoBlock(World world, int x, int y, int z, int newFlowDecay) - Flows into the block at the coordinates and
195     * changes the block type to the liquid.
196     */
197    private void flowIntoBlock(World par1World, int par2, int par3, int par4, int par5)
198    {
199        if (this.liquidCanDisplaceBlock(par1World, par2, par3, par4))
200        {
201            int i1 = par1World.getBlockId(par2, par3, par4);
202
203            if (i1 > 0)
204            {
205                if (this.blockMaterial == Material.lava)
206                {
207                    this.triggerLavaMixEffects(par1World, par2, par3, par4);
208                }
209                else
210                {
211                    Block.blocksList[i1].dropBlockAsItem(par1World, par2, par3, par4, par1World.getBlockMetadata(par2, par3, par4), 0);
212                }
213            }
214
215            par1World.setBlock(par2, par3, par4, this.blockID, par5, 3);
216        }
217    }
218
219    /**
220     * calculateFlowCost(World world, int x, int y, int z, int accumulatedCost, int previousDirectionOfFlow) - Used to
221     * determine the path of least resistance, this method returns the lowest possible flow cost for the direction of
222     * flow indicated. Each necessary horizontal flow adds to the flow cost.
223     */
224    private int calculateFlowCost(World par1World, int par2, int par3, int par4, int par5, int par6)
225    {
226        int j1 = 1000;
227
228        for (int k1 = 0; k1 < 4; ++k1)
229        {
230            if ((k1 != 0 || par6 != 1) && (k1 != 1 || par6 != 0) && (k1 != 2 || par6 != 3) && (k1 != 3 || par6 != 2))
231            {
232                int l1 = par2;
233                int i2 = par4;
234
235                if (k1 == 0)
236                {
237                    l1 = par2 - 1;
238                }
239
240                if (k1 == 1)
241                {
242                    ++l1;
243                }
244
245                if (k1 == 2)
246                {
247                    i2 = par4 - 1;
248                }
249
250                if (k1 == 3)
251                {
252                    ++i2;
253                }
254
255                if (!this.blockBlocksFlow(par1World, l1, par3, i2) && (par1World.getBlockMaterial(l1, par3, i2) != this.blockMaterial || par1World.getBlockMetadata(l1, par3, i2) != 0))
256                {
257                    if (!this.blockBlocksFlow(par1World, l1, par3 - 1, i2))
258                    {
259                        return par5;
260                    }
261
262                    if (par5 < 4)
263                    {
264                        int j2 = this.calculateFlowCost(par1World, l1, par3, i2, par5 + 1, k1);
265
266                        if (j2 < j1)
267                        {
268                            j1 = j2;
269                        }
270                    }
271                }
272            }
273        }
274
275        return j1;
276    }
277
278    /**
279     * Returns a boolean array indicating which flow directions are optimal based on each direction's calculated flow
280     * cost. Each array index corresponds to one of the four cardinal directions. A value of true indicates the
281     * direction is optimal.
282     */
283    private boolean[] getOptimalFlowDirections(World par1World, int par2, int par3, int par4)
284    {
285        int l;
286        int i1;
287
288        for (l = 0; l < 4; ++l)
289        {
290            this.flowCost[l] = 1000;
291            i1 = par2;
292            int j1 = par4;
293
294            if (l == 0)
295            {
296                i1 = par2 - 1;
297            }
298
299            if (l == 1)
300            {
301                ++i1;
302            }
303
304            if (l == 2)
305            {
306                j1 = par4 - 1;
307            }
308
309            if (l == 3)
310            {
311                ++j1;
312            }
313
314            if (!this.blockBlocksFlow(par1World, i1, par3, j1) && (par1World.getBlockMaterial(i1, par3, j1) != this.blockMaterial || par1World.getBlockMetadata(i1, par3, j1) != 0))
315            {
316                if (this.blockBlocksFlow(par1World, i1, par3 - 1, j1))
317                {
318                    this.flowCost[l] = this.calculateFlowCost(par1World, i1, par3, j1, 1, l);
319                }
320                else
321                {
322                    this.flowCost[l] = 0;
323                }
324            }
325        }
326
327        l = this.flowCost[0];
328
329        for (i1 = 1; i1 < 4; ++i1)
330        {
331            if (this.flowCost[i1] < l)
332            {
333                l = this.flowCost[i1];
334            }
335        }
336
337        for (i1 = 0; i1 < 4; ++i1)
338        {
339            this.isOptimalFlowDirection[i1] = this.flowCost[i1] == l;
340        }
341
342        return this.isOptimalFlowDirection;
343    }
344
345    /**
346     * Returns true if block at coords blocks fluids
347     */
348    private boolean blockBlocksFlow(World par1World, int par2, int par3, int par4)
349    {
350        int l = par1World.getBlockId(par2, par3, par4);
351
352        if (l != Block.doorWood.blockID && l != Block.doorIron.blockID && l != Block.signPost.blockID && l != Block.ladder.blockID && l != Block.reed.blockID)
353        {
354            if (l == 0)
355            {
356                return false;
357            }
358            else
359            {
360                Material material = Block.blocksList[l].blockMaterial;
361                return material == Material.portal ? true : material.blocksMovement();
362            }
363        }
364        else
365        {
366            return true;
367        }
368    }
369
370    /**
371     * getSmallestFlowDecay(World world, intx, int y, int z, int currentSmallestFlowDecay) - Looks up the flow decay at
372     * the coordinates given and returns the smaller of this value or the provided currentSmallestFlowDecay. If one
373     * value is valid and the other isn't, the valid value will be returned. Valid values are >= 0. Flow decay is the
374     * amount that a liquid has dissipated. 0 indicates a source block.
375     */
376    protected int getSmallestFlowDecay(World par1World, int par2, int par3, int par4, int par5)
377    {
378        int i1 = this.getFlowDecay(par1World, par2, par3, par4);
379
380        if (i1 < 0)
381        {
382            return par5;
383        }
384        else
385        {
386            if (i1 == 0)
387            {
388                ++this.numAdjacentSources;
389            }
390
391            if (i1 >= 8)
392            {
393                i1 = 0;
394            }
395
396            return par5 >= 0 && i1 >= par5 ? par5 : i1;
397        }
398    }
399
400    /**
401     * Returns true if the block at the coordinates can be displaced by the liquid.
402     */
403    private boolean liquidCanDisplaceBlock(World par1World, int par2, int par3, int par4)
404    {
405        Material material = par1World.getBlockMaterial(par2, par3, par4);
406        return material == this.blockMaterial ? false : (material == Material.lava ? false : !this.blockBlocksFlow(par1World, par2, par3, par4));
407    }
408
409    /**
410     * Called whenever the block is added into the world. Args: world, x, y, z
411     */
412    public void onBlockAdded(World par1World, int par2, int par3, int par4)
413    {
414        super.onBlockAdded(par1World, par2, par3, par4);
415
416        if (par1World.getBlockId(par2, par3, par4) == this.blockID)
417        {
418            par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, this.tickRate(par1World));
419        }
420    }
421
422    public boolean func_82506_l()
423    {
424        return false;
425    }
426}