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