summaryrefslogtreecommitdiffstats
path: root/src/Simulator/VanillaFluidSimulator.cpp
blob: 18d9b07e175bc7b66b78c31eb777f17b02877857 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

// VanillaFluidSimulator.cpp

#include "Globals.h"

#include "VanillaFluidSimulator.h"
#include "../World.h"
#include "../Chunk.h"
#include "../BlockArea.h"
#include "../Blocks/BlockHandler.h"
#include "../BlockInServerPluginInterface.h"





static const int InfiniteCost = 100;





cVanillaFluidSimulator::cVanillaFluidSimulator(
	cWorld & a_World,
	BLOCKTYPE a_Fluid,
	BLOCKTYPE a_StationaryFluid,
	NIBBLETYPE a_Falloff,
	int a_TickDelay,
	int a_NumNeighborsForSource
) : super(a_World, a_Fluid, a_StationaryFluid, a_Falloff, a_TickDelay, a_NumNeighborsForSource)
{
}





void cVanillaFluidSimulator::SpreadXZ(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta)
{
	// Calculate the distance to the nearest "hole" in each direction:
	int Cost[4];
	Cost[0] = CalculateFlowCost(a_Chunk, a_RelX + 1, a_RelY, a_RelZ,     X_PLUS);
	Cost[1] = CalculateFlowCost(a_Chunk, a_RelX - 1, a_RelY, a_RelZ,     X_MINUS);
	Cost[2] = CalculateFlowCost(a_Chunk, a_RelX,     a_RelY, a_RelZ + 1, Z_PLUS);
	Cost[3] = CalculateFlowCost(a_Chunk, a_RelX,     a_RelY, a_RelZ - 1, Z_MINUS);

	// Find the minimum distance:
	int MinCost = InfiniteCost;
	for (unsigned int i = 0; i < ARRAYCOUNT(Cost); ++i)
	{
		if (Cost[i] < MinCost)
		{
			MinCost = Cost[i];
		}
	}

	// Spread in all directions where the distance matches the minimum:
	if (Cost[0] == MinCost)
	{
		SpreadToNeighbor(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, a_NewMeta);
	}
	if (Cost[1] == MinCost)
	{
		SpreadToNeighbor(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, a_NewMeta);
	}
	if (Cost[2] == MinCost)
	{
		SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, a_NewMeta);
	}
	if (Cost[3] == MinCost)
	{
		SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, a_NewMeta);
	}
}





int cVanillaFluidSimulator::CalculateFlowCost(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, Direction a_Dir, unsigned a_Iteration)
{
	int Cost = InfiniteCost;

	BLOCKTYPE BlockType;
	NIBBLETYPE BlockMeta;

	// Check if block is passable
	if (!a_Chunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta))
	{
		return Cost;
	}
	if (
		!IsPassableForFluid(BlockType) ||                 // The block cannot be passed by the liquid ...
		(IsAllowedBlock(BlockType) && (BlockMeta == 0))  // ... or if it is liquid, it is a source block
	)
	{
		return Cost;
	}

	// Check if block below is passable
	if (!a_Chunk->UnboundedRelGetBlock(a_RelX, a_RelY - 1, a_RelZ, BlockType, BlockMeta))
	{
		return Cost;
	}
	if (IsPassableForFluid(BlockType) || IsBlockLiquid(BlockType))
	{
		// Path found, exit
		return a_Iteration;
	}

	// 5 blocks away, bail out
	if (a_Iteration > 3)
	{
		return Cost;
	}

	// Recurse
	if (a_Dir != X_MINUS)
	{
		int NextCost = CalculateFlowCost(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, X_PLUS, a_Iteration + 1);
		if (NextCost < Cost)
		{
			Cost = NextCost;
		}
	}
	if (a_Dir != X_PLUS)
	{
		int NextCost = CalculateFlowCost(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, X_MINUS, a_Iteration + 1);
		if (NextCost < Cost)
		{
			Cost = NextCost;
		}
	}
	if (a_Dir != Z_MINUS)
	{
		int NextCost = CalculateFlowCost(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, Z_PLUS, a_Iteration + 1);
		if (NextCost < Cost)
		{
			Cost = NextCost;
		}
	}
	if (a_Dir != Z_PLUS)
	{
		int NextCost = CalculateFlowCost(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, Z_MINUS, a_Iteration + 1);
		if (NextCost < Cost)
		{
			Cost = NextCost;
		}
	}

	return Cost;
}